Laravel assertDatabaseHas in phpunit test is not working

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Taming the Beast: Why assertDatabaseHas Fails in Laravel Tests

As a senior developer working with Laravel, we constantly grapple with ensuring our tests accurately reflect the state of the application's persistence layer. One of the most common stumbling blocks is when interactions occur via routes or controllers, and subsequent database assertions seem to contradict the expected outcome.

Today, we are diving into a specific frustration: why assertDatabaseHas seems to ignore the changes made by a recent request, even when you can verify the state directly on your Eloquent model. Let’s dissect this common pitfall and establish the robust methods for asserting database integrity in Laravel testing.

The Scenario: Model State vs. Database Reality

The code snippet you provided highlights a classic testing dilemma:

// ... setup where customer status_id is set to 2 (active)
$this->actingAs($this->hubAdminUser)
    ->patch(route('hub.customer.updateStatus', $this->customer)) // This should change status_id to 3
    ->assertRedirect();

// This assertion fails because the DB still shows 'status_id' = 2
$this->assertDatabaseHas('tenants', [
    'id' => $this->customer->id,
    'status_id' => 3, //deactivated
]);

You correctly observed that refreshing the model ($this->customer->refresh()->status_id) yields 3, confirming that the Eloquent object knows the intended state. However, when assertDatabaseHas fails, it signals a disconnect between the data retrieved by your test assertion and the actual data committed to the database during the request lifecycle.

Why Assertions Can Be Tricky in Laravel Testing

The failure often isn't due to the route update itself, but rather the timing or scope of the assertion relative to how Eloquent handles persistence within a test environment. Several factors can influence this:

  1. Transaction Scope: If your route logic is wrapped in database transactions (which is standard practice), and the outer test assertion isn't correctly observing the committed state, discrepancies can arise.
  2. Query Timing: Assertions are executed immediately after the preceding action (assertRedirect). While this seems logical, sometimes the database connection or the specific query being run by assertDatabaseHas needs an explicit nudge to ensure it fetches the absolute latest committed data.
  3. Model vs. DB Disconnect: Eloquent models represent objects, while assertions like assertDatabaseHas operate directly on the raw database layer. If there's any ambiguity about which object is in sync, relying solely on raw database checks provides the most definitive proof.

The Solution: Robust Assertions and Best Practices

The key to fixing this lies in trusting the raw database assertion while ensuring your test setup is isolated and clean. While you are already using assertDatabaseHas, we can refine the approach to make it bulletproof.

1. Trusting the Raw Query Assertion

When testing persistence, especially after controller interactions, relying on direct database queries ensures you are testing what actually exists in the table, bypassing any potential caching or model-level inconsistencies within the test framework itself. The method assertDatabaseHas is specifically designed for this purpose and should be your primary tool for verifying data integrity.

2. Ensuring Data Refresh (When Necessary)

If you find that assertions are still flaky, forcing a complete refresh of the model before the assertion can sometimes resolve timing issues, although in well-structured tests, this is usually redundant:

$this->actingAs($this->hubAdminUser)
    ->patch(route('hub.customer.updateStatus', $this->customer))
    ->assertRedirect();

// Explicitly refresh the model before checking the database state
$this->customer->refresh(); 

$this->assertDatabaseHas('tenants', [
    'id' => $this->customer->id,
    'status_id' => 3, //deactivated
]);

3. The Eloquent Way: Leveraging Relationships

For more complex assertions, especially when dealing with relationships (which is common in applications built on Laravel), consider using the power of Eloquent relationships to assert data indirectly. If you are working with models, ensuring that your model interactions correctly trigger persistence layer updates is crucial. For deep dives into how Eloquent manages these operations and database interaction patterns, exploring resources from laravelcompany.com is highly recommended.

Conclusion

Debugging assertions involving database state in Laravel testing often boils down to managing the synchronization between your Eloquent models and the underlying database. While model methods like refresh() are useful for debugging object states, direct assertions against the database using methods like assertDatabaseHas remain the gold standard for verifying persistence. By ensuring your setup is clean and trusting these raw queries, you can write tests that are accurate, reliable, and reflect the true state of your application.