1

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.

0

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this is what I wanted. It is clean and extendable to many other situations. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.