Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

nivv's avatar
Level 5

Bad test error output since Laravel 11 with nunomaduro/collision

On Laravel 10, when testing an API endpoint you could still see the underlying error:

    public function test_it_can_mark_a_quote_as_paused()
    {
        // Arrange
        $customer = Customer::factory()->create([
            'seller_id' => $this->user->id,
        ]);
        $quote = Quote::factory()->create([
            'customer_id' => $customer->id,
        ]);

        // Act
        $response = $this->json('POST', route('api.quotes.mark', $quote), [
            'accepted' => false,
            'declined' => false,
            'paused' => true,
        ]);

        // Assert
        $response->assertOk();
        $this->assertDatabaseHas('quotes', [
            'id' => $quote->id,
            'status' => QuoteStatus::PAUSED,
        ]);
        $this->assertDatabaseHas('quotes', [
            'id' => $quote->id,
            'accepted_at' => null,
        ]);
    }

Error output (notice "The following exception occured during the last request")

  • Tests\Feature\QuoteFeatureTest > it can mark a quote as paused
  Expected response status code [200] but received 500.
  Failed asserting that 500 is identical to 200.
  
  The following exception occurred during the last request:
  
  Error: Undefined constant App\Enums\QuoteStatus::PAUSEDD in /home/app/Http/Controllers/Api/MarkQuoteController.php:30

On Laravel 11 I only get this useless error:

   FAILED  Tests\Feature\LegacyBookingIntegrationFeatureTest > it can receive a payload from the front end and create…   
  Expected response status code [201] but received 500.
Failed asserting that 500 is identical to 201.

  at tests/Feature/LegacyBookingIntegrationFeatureTest.php:29
     25▕         // Act
     26▕         $response = $this->json('POST', route('api.booking.store'), $data);
     27▕ 
     28▕         // Assert
  ➜  29▕         $response->assertStatus(201);
     30▕         $response->assertJsonStructure([
     31▕             'data' => [
     32▕                 'order_number',
     33▕                 'customer_number',


  Tests:    1 failed (1 assertions)
  Duration: 0.19s

There is not way of knowing what went wrong, for that I have to manually put in $this->withoutExceptionHandling()) which is annoying to say the least.

Has this changed?

0 likes
2 replies
LaryAI's avatar
Level 58

It seems like you're experiencing a change in the way Laravel handles exception reporting in tests between Laravel 10 and Laravel 11. In Laravel 11, the detailed exception message might not be shown by default, which can make debugging more difficult.

To address this, you can use the withoutExceptionHandling method in your test to disable Laravel's exception handling and get more detailed error messages. However, as you mentioned, doing this manually for each test can be cumbersome.

A more efficient approach is to disable exception handling globally for all your tests. You can achieve this by adding the withoutExceptionHandling call in the setUp method of your test class or in a base test class that other test classes extend.

Here's how you can do it:

  1. Create a Base Test Class (if you don't already have one):
<?php

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication;

    protected function setUp(): void
    {
        parent::setUp();

        // Disable exception handling for all tests
        $this->withoutExceptionHandling();
    }
}
  1. Ensure Your Test Classes Extend the Base Test Class:
<?php

namespace Tests\Feature;

use Tests\TestCase;

class QuoteFeatureTest extends TestCase
{
    public function test_it_can_mark_a_quote_as_paused()
    {
        // Arrange
        $customer = Customer::factory()->create([
            'seller_id' => $this->user->id,
        ]);
        $quote = Quote::factory()->create([
            'customer_id' => $customer->id,
        ]);

        // Act
        $response = $this->json('POST', route('api.quotes.mark', $quote), [
            'accepted' => false,
            'declined' => false,
            'paused' => true,
        ]);

        // Assert
        $response->assertOk();
        $this->assertDatabaseHas('quotes', [
            'id' => $quote->id,
            'status' => QuoteStatus::PAUSED,
        ]);
        $this->assertDatabaseHas('quotes', [
            'id' => $quote->id,
            'accepted_at' => null,
        ]);
    }
}

By adding the withoutExceptionHandling call in the setUp method of your base test class, you ensure that all your tests will have exception handling disabled by default, providing you with more detailed error messages.

This approach should help you get the detailed error output you need without having to manually add withoutExceptionHandling to each test.

Please or to participate in this conversation.