Asserting Exceptions in Laravel Tests
Published on by Paul Redmond
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_responseThe 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_responseThe 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.