Skip to content

[9.x] Adds assertJsonIsArray and assertJsonIsObject for TestResponse - #45731

Merged
taylorotwell merged 3 commits into
laravel:9.xfrom
sethsandaru:assert-json-array-object
Jan 23, 2023
Merged

[9.x] Adds assertJsonIsArray and assertJsonIsObject for TestResponse #45731
taylorotwell merged 3 commits into
laravel:9.xfrom
sethsandaru:assert-json-array-object

Conversation

@sethsandaru

@sethsandaru sethsandaru commented Jan 20, 2023

Copy link
Copy Markdown
Contributor

Hi team,

When interacting with Collection, after several actions (like sort or filter,...), the array won't be an indexed array like before (I think most of us here would probably encounter this at least once).

And if we return the Collection immediately, it will result in a JS Object { ... }. For listing endpoints, it would be wrong and result in an error from the API consumers (frontend, mobile,...)

So to ensure we won't return the wrong data structure to the consumers, I added 2 assert methods to validate the data:

  • assertJsonIsArray
  • assertJsonIsObject

FYA: from 10.x we can use array_is_list (PHP 8.1) to check against the data since it requires PHP 8.2 and above.

Usage

$this->json('GET', 'countries')
    ->assertOk()
    ->assertJsonIsArray(); // [ {..}, {...}, ....]

$this->json('GET', 'users')
    ->assertOk()
    ->assertJsonIsArray('data'); // {'data': [...]}

$this->json('GET', 'countries/US')
    ->assertOk()
    ->assertJsonIsObject(); // {id: '...', name: '..'}

$this->json('GET', 'users/1')
    ->assertOk()
    ->assertJsonIsObject('data'); // {'data': {id: '..', name: '...'}}

PR Information

  • Implemented the methods
  • Added tests and cover all cases
  • No breaking changes - only introduces new methods

Additional information

assertJsonStructure won't help us to validate that (check TestResponseTest@testAssertJsonStructure)

The wildcard * only validates the repeating structure. It doesn't care whether our data is a JS array or JS object.

Screenshot 2023-01-20 at 10 14 01

assertJson or assertExactJson might be a good alternative, but for listing endpoints, preparing a big list of data to assert would not so fun, right?

Thanks all!

@timacdonald

Copy link
Copy Markdown
Member

I don't think this would handle empty objects / arrays well. It would just assume they are all arrays.

If the payload was...

{ "users": {} }

When you json encode that in PHP it is going to have to cast it to an object or an array based on flags.vvfg

@sethsandaru

Copy link
Copy Markdown
Contributor Author

@timacdonald Not sure if I understand you correctly.

So for Laravel Response, by default, if you pass an empty array response()->json([]) or new JsonResponse([]) => [] will be rendered => assertJsonIsArray is working correctly?

But for some cases, you wanted the users to be an object, then in your controller, you would do something like response()->json(new YourObject()); (if the object is empty => {} => assertJsonIsObject is working correctly?

@timacdonald
timacdonald self-requested a review January 23, 2023 03:24
@timacdonald

Copy link
Copy Markdown
Member

@sethsandaru I was concerned about how an empty hash in PHP is JSON encoded as an empty array, but I don't think that impacts this feature, and if anything this feature actually forces users to consider that.

Sorry for my rushed comment without further testing.

@timacdonald
timacdonald removed their request for review January 23, 2023 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants