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

Synchro's avatar

How to get more informative errors from PHPUnit?

Say I have a test that fails like this:

Expected status code 201 but received 422.
Failed asserting that 201 is identical to 422.
vendor/laravel/framework/src/Illuminate/Testing/TestResponse.php:186
tests/Unit/ProductTest.php:57

That tells me where it failed, but not why. In this example it's a validation failure, which could be caused by any of my rules, but I can't tell which from the information provided, yet I know that the response contains that info. PHPUnit aggressively suppresses output even during test failures (even killing dd output), so it's unnecessarily difficult to get more feedback on why something failed. As a workaround I'm forced to add manual logging calls to my actual code to dump response data so that I can see the actual error responses, which negates a lot of the point of having automated tests.

How can I get PHPUnit (or some aspect of my test setup) to produce more verbose output on failure?

0 likes
3 replies
Synchro's avatar

Unfortunately that doesn't help much. It shows a stack trace to the "where" at a slightly earlier point where the internal validation fails rather than when the external response status check fails in the test, and still doesn't show any more info about the "why" or other context, so it's really just a different phrasing of the same error:

Illuminate\Validation\ValidationException : The given data was invalid.
vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php:130
...
vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php:333
tests/Unit/ProductTest.php:54

I'm currently adding this to reveal the relevant info, but I don't really want to have to do this everywhere:

Log::debug(var_export($response->content(), true));
Tray2's avatar

When it comes to validations I usually do something like this

  /**
     * @test
     * @dataProvider storeValidationProvider
     * @param $fieldValue
     * @param $field
     */
    public function store_validation_tests($field, $fieldValue)
    {
        $book = Book::factory()->make([
            $field => $fieldValue
        ]);

        $response = $this->post('/books', $book->toArray());
        $response->assertStatus(302);
        $response->assertSessionHasErrorsIn($field);
    }

    public function storeValidationProvider()
    {
        return [
            'the title is required' => ['title', ''],
            'part must be numeric' => ['part', 'One'],
            'format_id is required' => ['format_id', ''],
            'format_id must exist in formats' => ['format_id', 100],
            'genre_id is required' => ['genre_id', ''],
            'genre_id must exist in genres' => ['genre_id', 100],
            'isbn is required' => ['isbn', ''],
            'invalid isbn10 cant be stored' => ['isbn', '123456789'],
            'invalid isbn13 cant be stored' => ['isbn', '9771234567890'],
            'released is required' => ['released', ''],
            'pages is required' => ['pages', ''],
            'pages must be numeric' => ['pages', 'Ten'],
            'blurb is required' => ['blurb', ''],
            'released cant be earlie than 1800' => ['released', 1799],
            'released cant be later than current year + 1' => ['released', Carbon::now()->addYear(2)->year],
            'reprinted cant be earlier than 1800' => ['reprinted',  1799],
            'reprinted cant be later than current year + 1' => ['reprinted', Carbon::now()->addYear(2)->year]
        ];
    }

So when thisd is 'the title is required' => ['title', ''] then I assert the the title field is in the errors

 $response->assertSessionHasErrorsIn($field);

Please or to participate in this conversation.