Summer Sale! All accounts are 50% off this week.

Synchro's avatar

assertJson weirdness - including actual in expected??

I've got a test that applies assertJson, and it's failing saying that the expected is not found in the actual part, however, when PHPUnit shows the difference between the two, the expected part has had the actual part prepended to it, so it always fails (unless expected is empty). This is very weird!

Response data:

[{
    "data": [
        {
            "type": "products",
            "id": "abc123",
            "attributes": {
                "status": "Ready",
        }
}]

My test:

$this->postJson(
    'api/products/dothing',
    [
        'id' => 'abc123',
        'contents'     => ['abc123'],
    ]
)
    ->assertJson(['type' => 'products'])

(notice that the data does indeed include 'type' => 'products')

PHPUnit expected value on the failure:

array (
  'data' => 
  array (
    0 => 
    array (
      'type' => 'products',
      'id' => 'abc123',
      'attributes' => 
      array (
        'serial' => 'abc123',
        'status' => 'Ready'
    ),
    'type' => 'products'
)

You can see that the actual value has been prepended to the expected one inside the data array (this is JSON:API), and the fragment I asked it to match fails because it's already been consumed by the actual. That's the only difference.

I have assertJson code working correctly in other very similar tests that are working fine, so this is evidently a specific case.

Does anyone have any idea what on earth might be going on?

Please ignore any minor discrepancies between the JSON and PHP versions of the test data; it's been edited from much larger output, and isn't relevant to the way this is failing.

0 likes
5 replies
LaryAI's avatar
Level 58

The issue is that the assertJson method is not matching the expected JSON fragment with the actual JSON fragment. Instead, it is prepending the actual JSON fragment to the expected JSON fragment and then trying to match it. This is causing the test to fail.

To fix this issue, you can use the assertJsonFragment method instead of assertJson. The assertJsonFragment method will only check if the expected JSON fragment is present in the actual JSON response, without trying to match the entire JSON response.

Here's an example of how you can use assertJsonFragment in your test:

$this->postJson(
    'api/products/dothing',
    [
        'id' => 'abc123',
        'contents' => ['abc123'],
    ]
)
->assertJsonFragment(['type' => 'products']);

This should fix the issue and make your test pass.

Synchro's avatar

@LaryAI Unfortunately that's not the problem. assertJson asserts that expected is a subset of actual, so this should match in this case. assertJsonFragment is much less flexible in its matching. It's effectively acting like this (not that you can do this):

assertEquals($actual . $expected, $actual)

Which will only ever be true if $expected is empty, which is the behaviour I'm seeing.

Synchro's avatar

Additional info. The PHPUnit output (not the diff, which uses PHP array syntax) appears like this:

Unable to find JSON: 

[{
    "type": "products"
}]

within response JSON:

[{
    "data": [
        {
            "type": "products",
            "id": "abc123",
            "attributes": {

So here, you can see that the expected value is correct, but when it's presented as a diff, the actual output is prepended.

FWIW, I'm using PHPUnit 10.2.1 on PHP 8.2.6, running in PHPStorm.

tisuchi's avatar

@synchro Your postJson() method suppose to be like this in your test method:

$this->postJson('api/products/dothing', [
    'id' => 'abc123',
    'contents' => ['abc123'],
])
    ->assertJson([
        'data' => [
            [
                'type' => 'products',
                'id' => 'abc123',
                'attributes' => [
                    'status' => 'Ready'
                ]
            ]
        ]
    ]);
Synchro's avatar

@tisuchi The data wrapper isn't necessary as the inner part still matches as a subset of the actual response (and I have hundreds of other passing tests that work fine). I also tried it and it just does the same thing.

Please or to participate in this conversation.