如何在PHPUnit测试中覆盖Laravel的异常处理程序?

时间:2016-02-15 22:12:20

标签: php unit-testing laravel exception-handling phpunit

我正在开发一个包含控制器的Laravel软件包,我希望对其进行单元测试。问题是如果在其中一个控制器中抛出异常,Laravel的异常处理程序会捕获它并输出500 HTTP响应。 PHPUnit不是更明智的,测试根本无法满足200 OK断言。 PHPUnit在本地和Travis CI等服务上输出缺少堆栈跟踪会极大地阻碍工作流程。

我知道我可以从\App\Exceptions\Handler这样的地方重新抛出异常,但由于这是一个包,我无法修改这些应用程序文件(laravel/laravel只是用于测试的依赖项,以便用于测试控制器的必要组件。

我原以为set_exception_handler()下面的TestCase调用会起作用,但奇怪的是它没有任何影响:

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

    set_exception_handler(function ($e) {
        throw $e;
    });

    return $app;
}

任何人都可以告诉我为什么以上不起作用吗?

2 个答案:

答案 0 :(得分:1)

单位测试应该测试单个单元,而不是整个应用程序。

您很有可能正在进行功能测试或集成测试。

当您测试控制器并期望200响应时,您需要知道的一切都是响应代码。 500响应未通过测试,表示故障或配置错误。 CI工具应该只显示通过/失败测试的数量。

测试不是调试,不应提供任何特殊的回溯。如果您认为自己没有足够的有关错误性质的信息,则应该检查错误处理和日志记录。否则,当生产中发生错误时,您将面临缺乏信息的同样问题。

答案 1 :(得分:0)

您尝试重置您失败的异常处理程序(因为您已经发现)。当您使用call()方法发出请求时,Laravel会创建Http内核的新实例,当它处理新请求时,它会自行引导并运行HandleExceptions引导程序,当然这会重置再次处理异常处理程序。

您可能无权修改\App\Exceptions\Handler课程,但您不需要。您需要做的就是创建自己的异常处理程序,然后更新IoC容器中的绑定以使用异常处理程序,而不是Laravel应用程序提供的异常处理程序。

首先,创建新的异常处理程序:

<?php

class MyExceptionHandler extends \App\Exceptions\Handler
{
    public function render($request, \Exception $e)
    {
        // You may want to keep all these cases for special exceptions.
        // If not, just get rid of these lines.
        $e = $this->prepareException($e);

        if ($e instanceof HttpResponseException) {
            return $e->getResponse();
        } elseif ($e instanceof AuthenticationException) {
            return $this->unauthenticated($request, $e);
        } elseif ($e instanceof ValidationException) {
            return $this->convertValidationExceptionToResponse($e, $request);
        }

        // Not a special exception. Just re-throw it to get meaningful output.
        throw $e;
    }
}

现在,在您的createApplication()方法中,更新异常处理程序绑定:

public function createApplication()
{
    $app = require __DIR__.'/../bootstrap/app.php';

    // Tell the tests to use your exception handler.
    $app->singleton(\Illuminate\Contracts\Debug\ExceptionHandler::class, MyExceptionHandler::class);

    $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();

    return $app;
}