Featured image of post Laravel Testing: Assert View Data Without Parsing HTML

Laravel Testing: Assert View Data Without Parsing HTML

HTML assertions on complex views are brittle. Use viewData() to assert view variables directly, and compare Models by id or toArray() to avoid identity issues.

When dealing with complex pages in Laravel Feature Tests, testing HTML directly is painful. Instead, you can test just the data passed to the View.

Use viewData to Retrieve View Variables

$response->viewData('key') returns the variable passed to the View:

1
2
3
4
5
6
// routes/web.php
Route::get('/', function () {
    return view('welcome', [
        'foo' => 'bar',
    ]);
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    public function test_only_view_data(): void
    {
        $response = $this->get('/')->assertOk();

        self::assertEquals('bar', $response->viewData('foo'));
    }
}

Watch Out for Object Identity When Comparing Models

When View data contains a Model fetched from the database, comparing two Model objects directly with assertEquals will fail:

1
2
3
4
5
6
7
8
9
// routes/web.php
Route::get('/', function () {
    $user = User::firstOrFail();

    return view('welcome', [
        'foo' => 'bar',
        'user' => $user,
    ]);
});
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class ExampleTest extends TestCase
{
    use RefreshDatabase;

    public function test_only_view_data(): void
    {
        /** @var User $user */
        $user = User::factory()->create();

        $response = $this->get('/')->assertOk();

        self::assertEquals('bar', $response->viewData('foo'));
        // This fails because the route re-fetches from DB -- it's not the same object
        self::assertEquals($user, $response->viewData('user'));
    }
}

The route fetches the User from the database again. Even though the data is the same, it’s not the same object instance. Compare attributes instead:

1
2
self::assertEquals($user->id, $response->viewData('user')->id);
self::assertEquals($user->toArray(), $response->viewData('user')->toArray());

viewData only verifies that data was correctly passed to the View. It cannot test whether JavaScript, CSS, or Blade templates render the variables correctly on the frontend.