【问题标题】:how to make PHPUnit stop intercepting exceptions?如何让 PHPUnit 停止拦截异常?
【发布时间】:2015-11-02 11:34:00
【问题描述】:

我正在使用一个内部框架,其中每个异常都由错误处理程序捕获并以适当的 JSON 错误响应返回,适用于 RESTFul API。

然后我有一套测试,它们是 API 测试,主要测试 API 是否返回带有预期错误代码的正确 JSON 响应。

对于每个测试,都会修改(然后恢复)全局变量以模拟不同的 HTTP 请求。我这样做是为了避免进行 cURL 测试(通过 Guzzle 或类似方法)的过载,并导致在 CLI 环境下,代码不知道服务器的 url。

<?php
// ... example, part of a base ApiTestCase class:

// Override globals (should be backed up by PHPUnit)
$_SERVER['REQUEST_METHOD']     = $request->method;
$_SERVER['QUERY_STRING']       = http_build_query($request->parameters);
$_SERVER['PATH_INFO']          = $request->path;
$_SERVER['REQUEST_URI']        = $request->path . ($_SERVER['QUERY_STRING'] ? '?' : '') . $_SERVER['QUERY_STRING'];
$_SERVER['REQUEST_TIME']       = time();
$_SERVER['REQUEST_TIME_FLOAT'] = microtime(true);
$_SERVER['HTTP_COOKIE']        = '';

// Set headers, cookies and parameters
foreach ($request->headers as $k => $v) {
  $_SERVER['HTTP_' . strtoupper(str_replace('-', '_', trim($k)))] = $v;
}
if ($_SERVER['HTTP_COOKIE']) {
  $GLOBALS['_COOKIE'] = http_parse_cookie($_SERVER['HTTP_COOKIE']);
} else {
  $GLOBALS['_COOKIE'] = [];
}
$GLOBALS['_REQUEST'] = $request->parameters;

$responseBody = $app->start();

$response->httpCode = http_response_code();
$response->body     = $responseBody ? @json_decode($responseBody) : null;
$response->headers  = headers_list();

(我知道这样改变全局变量不好,框架不应该直接依赖全局变量,但我仍然要处理遗留代码。)

那么问题来了:当我尝试测试 JSON 错误响应时:PHPUnit 拦截了抛出的异常(在我开头提到的处理程序之前),因此框架没有机会将其转换为 JSON 并返回正确的响应.

我试图在 PHPUnit 手册中找到一些内容来禁用 PHPUnit 错误处理程序,但没有成功。

在这种情况下我该怎么办?谢谢

【问题讨论】:

  • 请注意,您引用的大部分代码看起来应该在您的 phpUnit 测试类中的 setup() 函数中。另外,请注意 phpUnit 具有在每次测试后将全局变量恢复到其原始状态的内置功能。您可能想利用它而不是重新发明轮子。
  • 代码只是一个示例,但 PHPunit 确实会进行全局备份
  • @mjolnic 嗨,伙计,我在这里遇到了完全相同的问题。您找到合适的分辨率了吗?
  • 我实现的唯一可行的解​​决方案是不依赖异常处理程序来发送错误响应,而是在应用程序的顶层捕获异常。

标签: php json api phpunit web-api-testing


【解决方案1】:

为了清楚起见,听起来我们实际上并没有在这里讨论捕获异常;我们正在讨论使用 PHP 的 set_error_handler() 在终止程序之前拦截致命错误。这将处理错误和未捕获的异常。

你不能做的一件事是让这些错误和异常落入你的错误处理函数——正如你已经发现的那样,phpUnit 会执行你自己无法覆盖的错误处理(因为它是对 phpUnit 的工作原理有点基础)。

你要做的就是告诉 phpUnit 你期待什么样的异常或错误;然后根据是否发生错误,您的测试将通过或失败。您不会运行错误处理程序,但实际上,您不需要这样做;如果需要,您可以单独测试该功能。对于错误情况,您不需要看到错误处理程序每​​次都产生正确的输出,只需发生会触发处理程序的错误即可。

对于常规的 PHP 异常,您可以在测试函数上方使用 phpUnit 的 @expectedException 注释,如下所示:

/**
 * @expectedException YourExpectedExceptionClass
 */
function testThisWillThrowAnException() {
    ....
}

如果 PHP 代码预期会产生 PHP 错误(即错误,而不是异常),那么您将使用相同的想法,但 phpUnit 为错误提供了一个帮助类名称:PHPUnit_Framework_Error。所以你的代码看起来像这样:

/**
 * @expectedException PHPUnit_Framework_Error
 */
function testThisWillProduceAPHPError() {
    ....
}

无论哪种情况,如果发生预期的错误/异常,您的测试将通过。

您还可以测试特定的异常消息和代码,以防异常类本身不足以让您知道测试是否完成了您希望它执行的操作。请参阅the phpUnit manual page for annotations 了解更多信息。

【讨论】:

  • 谢谢,问题是我已经在单元测试(正在测试源代码和行为)中测试了这些异常,我不想复制它。我在这里要做的是测试最终响应:客户端实际得到什么,这在 PHPUnit 中似乎是不可能的,而不依赖于 cURL,这会减慢测试的执行速度并且不允许您模拟源代码或数据源。
【解决方案2】:

上面的例子也是正确的,我只提供了异常作为断言,并让你了解异常的工作原理。

/**
 * @dataProvider fixturesProvider // its just example
 */
public function testDataIsWrong($fixtures)
{
    try
    {
        //Some Code
        $this->fail('Exception');
    }
    catch(Exception $ex)
    {
        $this->assertEquals($ex,'Exception');
    }
}

这也为您的代码提供了可能性,您可以测试错误或不正确的数据并断言它不正确。

【讨论】:

  • OP 试图说明的一点是异常已经在他的应用程序中被捕获,并在那里处理。当他的 API 响应时,所有异常都转换为 JSON 响应,他想检查响应是否正确。在他的测试中添加更多异常处理只会处理症状,但完全没有抓住重点,并且不允许测试作为目标的 JSON 响应。
【解决方案3】:

我实现的唯一解决我的问题的解决方案是不委托异常处理程序负责构建和发送 API 错误响应,而是在应用程序的顶层捕获异常。

在 catch 中,我有一个异常到错误的响应转换器来处理这个问题(或者在方便时重新抛出异常),所以不重要的错误(比如产生 HTTP 4xx 响应的错误)是不再出现在 PHPUnit 测试中。 我的 PHPUnit 测试现在也能够处理 PSR-7 HTTP 响应对象,而不是捕获输出缓冲区。

【讨论】:

    猜你喜欢
    • 2016-03-14
    • 1970-01-01
    • 2015-08-29
    • 2014-07-19
    • 2011-05-13
    • 2014-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多