Home » Php » php – Handling Laravel HttpException in Unit Testing

php – Handling Laravel HttpException in Unit Testing

Posted by: admin July 12, 2020 Leave a comment

Questions:

One of my test functions for unit testing in Laravel keeps erroring out. I’m trying to assert that requesting a specific page without certain conditions being met triggers a 403 FORBIDDEN error.

My test case function is this:

public function testNoAjaxCall() {

    $this->call('POST', 'xyz', array());

    $this->assertResponseStatus(403);

}

In the controller action this is routed to, I have this:

if(!Input::has('ajax') || Input::get('ajax') != 1) {

    // Drop all 'non-ajax' requests.
    App::abort(403, 'Normal POST requests to this page are forbidden. Please explicitly tell me you\'re using AJAX by passing ajax = 1.');

}

Running phpunit returns with the following:

1) RaceTest::testNoAjaxCall

Symfony\Component\HttpKernel\Exception\HttpException: Normal POST requests to this page are forbidden. Please explicitly tell me you’re using AJAX by passing ajax = 1.

[path\to\laravel]\vendor\laravel\framework\src\Illuminate\Foundation\Application.php:875

[redacted stack trace] [path\to\laravel]\vendor\symfony\http-kernel\Symfony\Component\HttpKernel\Client.php:81

[path\to\laravel]\vendor\symfony\browser-kit\Symfony\Component\BrowserKit\Client.php:325

[path\to\laravel]\vendor\laravel\framework\src\Illuminate\Foundation\Testing\TestCase.php:74

[path\to\laravel]\app\tests[testFileName].php:176

FAILURES!

Tests: 6, Assertions: 17, Errors: 1.

Of course the stack trace points back to $this->call(..); and App::abort(..)

I have a HttpException error handler in my app/start/global.php file, which works when triggering it outside of unit testing (e.g. making a POST request directly to the tested URL), but unit testing doesn’t seem to correctly catch the exception or even reach the assert call.

What am I missing?

How to&Answers:

http://phpunit.de/manual/3.7/en/appendixes.annotations.html#appendixes.annotations.expectedException should already explain it.

/**
 * @expectedException \Symfony\Component\HttpKernel\Exception\HttpException
 * @expectedExceptionMessage Normal POST requests to this page are forbidden. Please explicitly tell me you\'re using AJAX by passing ajax = 1.
 */
public function testNoAjaxCall() {

    $this->call('POST', 'xyz', array());

}

Answer:

As of now, the best way I have found to assert HttpException status codes can be found in sirbarrence’s work around at https://github.com/laravel/framework/issues/3979

TestCase.php:

public function assertHTTPExceptionStatus($expectedStatusCode, Closure $codeThatShouldThrow)
{
    try 
    {
        $codeThatShouldThrow($this);

        $this->assertFalse(true, "An HttpException should have been thrown by the provided Closure.");
    } 
    catch (\Symfony\Component\HttpKernel\Exception\HttpException $e) 
    {
        // assertResponseStatus() won't work because the response object is null
        $this->assertEquals(
            $expectedStatusCode,
            $e->getStatusCode(),
            sprintf("Expected an HTTP status of %d but got %d.", $expectedStatusCode, $e->getStatusCode())
        );
    }
}

Usage:

public function testGetFailsWith403()
{
    $this->assertHTTPExceptionStatus(403, function ($_this)
    {
        $_this->call('GET', '/thing-that-should-fail');
    });
}