Ariel Mejia
Posted on July 21, 2024
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" ...>
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);
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",
])
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);
});
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",
]);
});
This assertion is short, reusable, easy to read and use:
$this->get("contact-us")->assertCSRFTokenExists();
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
Posted on July 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.