如何让PHPUnit停止拦截异常?

how to make PHPUnit stop intercepting exceptions?

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

然后我有一套测试,它们是 API 测试,主要是测试 API return 是否具有预期的正确 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 和 return 正确的回应。

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

在这种情况下我能做什么?谢谢

澄清一下,听起来我们实际上并不是在谈论捕获异常;我们正在谈论使用 PHP 的 set_error_handler() 在程序终止之前拦截致命错误。这将处理错误和未捕获的异常。

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

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

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

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

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

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

无论哪种情况,如果出现预期的 error/exception,您的测试就会通过。

您还可以测试特定的异常消息和代码,以防异常 class 本身的信息不足以让您了解测试是否完成了您希望它做的事情。有关详细信息,请参阅 the phpUnit manual page for annotations

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

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

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

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

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