1

In my application I have a response like this:

{
  "items": [
    {
      "id": 10,
      "field": "foo"
    },
    {
      "id": 20,
      "field": "bar"
    }
  ]
}

I need to test the content of items and validate each id.

I've tried many solutions but no one works, for example (this is just a kind of pseudo-code):

assertJson(fn (AssertableJson $json) =>
    $json->where('items.*.id', [10, 20])
)

Is there a way to use a wildcard to pick every ID and validate using an array?

2
  • Is this PHPUnit? Commented Aug 19, 2022 at 10:39
  • I'm using Pest, but is built on top of PHP unit so i can use both. This syntaxt come from Laravel HTTP fluent json testing anyway Commented Aug 19, 2022 at 10:45

2 Answers 2

2

The lack of support for testing an array of randomly ordered elements is pretty frustrating. I just came across this problem and solved it by looping over the array of things I was looking for and making sure they're all in the response data. Fortunately there's an AssertableJson::toArray() method which allows access to the JSON directly.

<?php

namespace Tests\Feature;

use Illuminate\Testing\Fluent\AssertableJson;
use Symfony\Component\HttpFoundation\Response;

use Tests\TestCase;

class ReportingTest extends TestCase
{
    public function testReportData(): void
    {
        $expected_ids = [10, 20];
        $this->getJson(route("report.view"))
            ->assertStatus(Response::HTTP_OK)
            ->assertJsonIsObject()
            ->assertJson(function (AssertableJson $response) use ($expected_ids) {
                $response
                    ->has("items", 2)
                    ->has("items", function (AssertableJson $items) use ($expected_ids) {
                        $item_ids = array_column($items->toArray(), "id");
                        foreach ($expected_ids as $id) {
                            $this->assertContains($id, $item_ids);
                        }
                    });
            });
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You can use array_filter:

$idArray = [10, 20];

$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object

// Filter the items for items that aren't in the ID list
$invalidItems = array_filter($items, function ($el) {
    // If the item has an id which isn't in the array, return true
    return !in_array($el["id"], $idArray);
});

// This returns true if we found 0 items with IDs not in the ID list
return $invalidItems == [];

You can similarly use array_map to simplify your array, then compare it to your ID array:

$myObj = json_decode($json); // Turn JSON to obj
$items = $myObj["items"]; // Get items from object

$outIdArray = array_map(function($el) {
    return $el["id"];
}, $items);

// Compare $outIdArray to [10, 20]

2 Comments

Of course, array_column() is designed for the task that you are using array_map() for. array_udiff() can be used in the first snippet. I am surprised that json_decode() produces $myObj["items"].
@mickmackusa Good point, I don't think I ever ended up using array_column myself.

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.