Asserting Exceptions in Laravel Tests

Laravel's recent release of Laravel 11.4 introduced the Exceptions facade, which adds conveniences around asserting exceptions in Laravel's exception handler. Before this release, I would typically use the $this->withoutExceptionHandling() to assert that a specific exception happened during an HTTP test:

use App\Exceptions\WelcomeException;

$this->withoutExceptionHandling();

try {
    $this->get('/');
} catch (WelcomeException $e) {
    $this->assertEquals('Woops, there was an issue with your request!', $e->getMessage());

    return;
}

$this->fail(sprintf('The expected "%s" exception was not thrown.', WelcomeException::class));

When you expect a request to not throw any exceptions, using withoutExceptionHandling cuts out the middleman when you're debugging why an error is happening when you don't expect it. The above code is tedious to write, because it manually captures the exception, makes assertions about the exception, and calls return to avoid the manual $this->fail() call. The manual failure will catch situations when the test doesn't throw an exception when expected.

If $this->fail() is called in the above scenario, here's what the output would look like:

$ phpunit

There was 1 failure:

1) Tests\Feature\ExampleTest::test_the_application_returns_a_successful_response
The expected "App\Exceptions\WelcomeException" exception was not thrown.

/app/tests/Feature/ExampleTest.php:33

Laravel's Exceptions Facade

Let's look at how the new Exceptions facade can simplify our test; the first example, rewritten, would look as follows:

use Illuminate\Support\Facades\Exceptions;

Exceptions::fake();

$this->get('/');

Exceptions::assertReported(function (WelcomeException $e): bool {
    return $e->getMessage() === "Woops, there was an issue with your request!";
});

Using the Exceptions facade gives us the bonus of not having to capture an exception to assert things manually. Said another way, the test can keep Laravel's exception handler in place but still be able to assert exceptions that happened during a request.

If we want to be sure that a test doesn't throw a specific exception or doesn't throw any exceptions, period, the new facade has our back:

Exceptions::assertNotReported(WelcomeException::class);
 
Exceptions::assertNothingReported();

If the exception handler does not report the WelcomeException, the test output would give us a nicely formatted message:

$ phpunit

There was 1 failure:

1) Tests\Feature\ExampleTest::test_the_application_returns_a_successful_response
The expected [App\Exceptions\WelcomeException] exception was not reported.

While there may be times when you don't want to fake Laravel's exception handler, when testing edge cases, the new Exceptions facade is tremendously helpful and cleans up our code:

Exceptions::assertReported(WelcomeException::class);
Exceptions::assertReportedCount($count);
Exceptions::assertNotReported(WelcomeException::class);
Exceptions::assertNothingReported();
Exceptions::throwFirstReported();

To learn more about the Exceptions facade, check out Laravel’s documentation.


The post Asserting Exceptions in Laravel Tests appeared first on Laravel News.

Join the Laravel Newsletter to get all the latest Laravel articles like this directly in your inbox.