简体   繁体   English

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

[英]How can I override Laravel's exception handler in PHPUnit tests?

I am developing a Laravel package that includes controllers, which I wish to unit test. 我正在开发一个包含控制器的Laravel软件包,我希望对其进行单元测试。 The problem is if an exception is thrown inside one of these controllers, Laravel's exception handler captures it and outputs a 500 HTTP response. 问题是如果在其中一个控制器中抛出异常,Laravel的异常处理程序会捕获它并输出500 HTTP响应。 PHPUnit is none the wiser, and tests simply fail to meet the 200 OK assertion. PHPUnit不是更明智的,测试根本无法满足200 OK断言。 The lack of a stack trace in PHPUnit's output both locally and on services such as Travis CI hinders workflow enormously. PHPUnit输出中缺少堆栈跟踪本地和Travis CI等服务都极大地阻碍了工作流程。

I'm aware that I could rethrow the exception from somewhere such as \\App\\Exceptions\\Handler , but since this is a package, I can't modify those application files ( laravel/laravel is simply a dependency for testing, in order to rope in the necessary components for testing controllers). 我知道我可以从诸如\\App\\Exceptions\\Handler地方重新抛出异常,但由于这是一个包,我无法修改那些应用程序文件( laravel/laravel只是一个依赖于测试,以便用于测试控制器的必要组件中的绳索)。

I would have thought the set_exception_handler() call below in TestCase would work, but weirdly it has no impact whatsoever: set_exception_handler()以为TestCase下面的set_exception_handler()调用会起作用,但奇怪的是它没有任何影响:

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;
}

Can anyone tell me why the above doesn't work? 谁能告诉我为什么以上不起作用?

Unittests should test individual units, not the whole application. 单元测试应该测试单个单元,而不是整个应用程序。

There are great chances you are doing functional or integration testing. 您很有可能正在进行功能或集成测试。

When you test controllers, and expect 200 response, everything you need to know is response code. 当您测试控制器并期望200响应时,您需要知道的一切都是响应代码。 500 response fails the test, which indicates malfunction or misconfiguration. 500响应未通过测试,表示故障或配置错误。 CI tool should only show number of passed/failed tests. CI工具应该只显示通过/失败测试的数量。

Testing is not debugging and should not provide any special backtraces. 测试不是调试,不应提供任何特殊的回溯。 If you feel you don't have enough information about nature of the error, you should review your error handling and logging. 如果您认为自己没有足够的有关错误性质的信息,则应该检查错误处理和日志记录。 Otherwise you will face the same problem with lack of information when errors happen in production. 否则,当生产中发生错误时,您将面临缺乏信息的同样问题。

Your attempt to reset the exception handler where you did won't work (as you've already found out). 您尝试重置您所执行的异常处理程序将无法正常工作(正如您已经发现的那样)。 When you use the call() method to make a request, Laravel makes a new instance of the Http Kernel, and when it handles the new request, it bootstraps itself and runs the HandleExceptions bootstrapper, which of course resets the exception handler again. 当您使用call()方法发出请求时,Laravel会创建一个Http Kernel的新实例,当它处理新请求时,它会自行引导并运行HandleExceptions引导程序,这当然会再次重置异常处理程序。

You may not have access to modify the \\App\\Exceptions\\Handler class, but you don't need to. 您可能无权修改\\App\\Exceptions\\Handler类,但您不需要。 All you need to do is create your own exception handler, and then update the binding in the IoC container to use your exception handler, instead of the one provided by the Laravel application. 您需要做的就是创建自己的异常处理程序,然后更新IoC容器中的绑定以使用异常处理程序,而不是Laravel应用程序提供的异常处理程序。

So, first, create your new exception handler: 首先,创建新的异常处理程序:

<?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;
    }
}

Now, in your createApplication() method, update the exception handler binding: 现在,在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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM