As you know when in PHPUnit we use assertEquals()
and assertSame()
and we pass arrays to them, they will assert arrays based on key-value pairs. So if the array is a non-assoc array (list) and the order doesn't matter, the tests will fail, since these methods consider the index as a key and compare the value for each corresponding key.
This problem can be easily fixed by a custom assertion method like this:
protected function assertListWithoutOrderEquals(array $expected, array $actual): void
{
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
}
But when we are in the Laravel HTTP test and we have JSON to assert, Laravel will convert JSON to an array and assert them based on these methods, I think. I don't have any idea how to fix this problem here.
For example, I have this test in Laravel and I have a problem with asserting genres
value.:
use Illuminate\Testing\Fluent\AssertableJson;
public function test_http_response(): void
{
$expectedData = [
'id' => 1,
'name' => 'The fantastic book',
'genres' => ['science-fiction', 'drama', 'mystery'],
// other elements
];
$response = $this->get('url');
$response->assertJson(
fn(AssertableJson $json) => $json->where('id', $expectedData['id'])
->where('name', $expectedData['name'])
->where('genres', $expectedData['genres']) // This is order sensitive and makes tests to fail.
->etc()
);
}
I tried this but it's messy. I'm looking for a better and cleaner solution if you can help me.
->where('genres', fn($genres) => $genres->diff($expectedData['genres'])->isEmpty() && $genres->count() == count($expectedData['genres']))
To explain better, Laravel will convert the JSON array to the collection, so I checked diff()
and since diff() is a one-way method, I mean it checks that all items in the first array exist in the second array and don't consider extra items in the second array, I checked the size of them as well.
I think a callback will be necessary in this case but you can take advantage of the same logic that PHPUnit uses to implement assertEqualsCanonicalizing
$expectedData = [
'id' => 1,
'name' => 'The fantastic book',
'genres' => ['science-fiction', 'drama', 'mystery'],
// other elements
];
$result = $this->get('url');
$result->assertJson(
fn(AssertableJson $json) => $json->where('id', $expectedData['id'])
->where('name', $expectedData['name'])
->where('genres', fn ($val) => (new IsEqualCanonicalizing($val->all()))->evaluate($expectedData['genres']))
);
I'm not sure if this is the "best" way but it is "a" way.