Test Your DOM in Laravel with PHPUnit

arielmejiadev

Ariel Mejia

Posted on July 21, 2024

Test Your DOM in Laravel with PHPUnit

PHPUnit comes out of the box with features like AssertSee and AssertSeeText in both cases we can assert against a specific text, to assert HTML tags we are going to rely on these features with some custom work to polish these a little bit more.

Case of use

I need to test that some forms generate a CSRF token.

Basic Solution

Laravel CSRF token directive generates an input like this:

<input type="hidden" name="_token" value="random_generated_token" ...>
Enter fullscreen mode Exit fullscreen mode

As the generated token changes between requests we are not going to test the generated token value, instead we are going to assert that the input exists with some of the attributes required, PHPUnit assertSee has second param to escape a value so we can do something like this:

$this
    ->get("contact-us")
    ->assertSee([
        '<input name="some_database_column"'
    ], false);
Enter fullscreen mode Exit fullscreen mode

Improving iteration

It would solve our solution, but probably there is a better way to use this for more cases, so writing using a "wishful thinking" approach my desired code would be something like this:

$this->get("contact-us")
    ->assertHtml('input', [
        "type" => "hidden",
        "name" => "_token",
])
Enter fullscreen mode Exit fullscreen mode

This would be useful as it adds an assertion that would work in multiple cases by only passing the tag name and an array of attributes

We can add something like this in Laravel by adding a custom macro to the TestResponse class in the AppServiceProvider or any other custom Provider:

TestResponse::macro('assertHtml', function ($tag, $attributes) {
    $attributes = collect($attributes)
        ->map(function ($attributeValue, $attributeKey) {
            return "$attributeKey=\"$attributeValue\" ";
        })
        ->values()
        ->implode("", "");

        $htmlElement = "<$tag $attributes";
        $this->assertSee([$htmlElement], false);
});
Enter fullscreen mode Exit fullscreen mode

Now we can test our DOM by testing the presence of a tag and an attribute.

Aiming Laravelish Way

We can go a little bit forward in this case, I would need to assert that a form as CSRF tokens in multiple forms in the app, so we can rely on our macro to create more assertions, in this case as the expected HTML tag and attributes would not change I can add something like this:

TestResponse::macro('assertCSRFTokenExists', function () {
    $this->assertHtml('input', [
        "type" => "hidden",
        "name" => "_token",
    ]);
});
Enter fullscreen mode Exit fullscreen mode

This assertion is short, reusable, easy to read and use:

$this->get("contact-us")->assertCSRFTokenExists();
Enter fullscreen mode Exit fullscreen mode

For more powerful assertions you should consider an excellent package like:

sinnbeck/laravel-dom-assertions

Hopefully, this helps to add basic DOM assertions in your tests

💖 💪 🙅 🚩
arielmejiadev
Ariel Mejia

Posted on July 21, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related