Here's an interesting scenario I bumped into recently.
Let's say you want to test an API response and your value includes a backed enum:
// app/Enums/Urgency.php
enum Urgency: string {
case LOW = 'low';
case MEDIUM = 'medium';
case HIGH = 'high';
}
// tests/Feature/ExampleTest.php
$response->assertJson([
'data' => [
'urgency' => Urgency::LOW,
],
])
This test will fail, because the backed enum doesn't match the string value in the JSON response.
It's an easy fix, just add ->value
to the enum and your test passes. No big deal!
But here's where it gets interesting. Look closely at the test failure error message:
Unable to find JSON:
[{
"data": {
"urgency": "low"
}
}]
within response JSON:
[{
"data": {
"urgency": "low"
}
}]
The two values match! And this example is extremely simple, so the weirdness is easier to spot, but now imagine it within a response with dozens of keys and values.
What is going on?
Inside Laravel's assertJson
test method, it defers the assertion to PHPUnit::assertArraySubset
.
As expected the PHPUnit method sees the BackedEnum
object and the string value as different, so the test fails as it should.
But when Laravel calls that PHPUnit assertion, it also passes in the error message to show if the assertion fails.
And to make the error message more nicely-formatted, it formats both the expected and actual values with json_encode
with pretty printing.
When you json_encode
a BackedEnum
object, it will return the string value of the enum, not the enum object itself.
So the test is failing, but the error message looks like it should have passed.
Here to help,
Joel
P.S. Despite the occasional confusing error message, testing is one of the single best things you can do to keep your app easy to maintain. Would you like some help introducing testing to your app?