Currently, I'm using assertJsonStructure
in order to validate the structure, but it doesn't check the exact JSON structure, for example:
[
{"id":1, "name": "Bueno Aires", "slug": "buenos-aires"},
{"id":2, "name": "Neuqu茅n", "slug": "neuquen"},
{"id":3, "name": "C贸rdoba", "slug": "cordoba"}
]
Test results are:
assertJsonStructure([['id', 'name', 'slug']]);
: true
assertJsonStructure([['id', 'name']]);
: true
Test results expected:
assertJsonStructure([['id', 'name', 'slug']]);
: true
assertJsonStructure([['id', 'name']]);
: false
Is there some way to validate that?
Sorry please ask on the forums, this repo is for bug reporting only.
thanks @themsaid
For future related issues, https://laravel.io/forum/assert-exact-json-structure
Here is my solution.
<?php
namespace Tests\Support;
use Illuminate\Foundation\Testing\TestResponse;
use PHPUnit\Framework\Assert as PHPUnit;
trait AssertJson
{
public function setUpAssertJson()
{
TestResponse::macro('assertJsonStructureExact', function (array $structure = null, $responseData = null)
{
if (is_null($structure)) {
return $this->assertJson($this->json());
}
if (is_null($responseData)) {
$responseData = $this->decodeResponseJson();
}
if (! array_key_exists('*', $structure)) {
$keys = array_map(function ($value, $key) {
return is_array($value) ? $key : $value;
}, $structure, array_keys($structure));
PHPUnit::assertEquals($keys, array_keys($responseData));
}
foreach ($structure as $key => $value) {
if (is_array($value) && $key === '*') {
PHPUnit::assertInternalType('array', $responseData);
foreach ($responseData as $responseDataItem) {
$this->assertJsonStructureExact($structure['*'], $responseDataItem);
}
} elseif (is_array($value)) {
PHPUnit::assertArrayHasKey($key, $responseData);
$this->assertJsonStructureExact($structure[$key], $responseData[$key]);
} else {
PHPUnit::assertArrayHasKey($value, $responseData);
}
}
return $this;
});
}
}
Usage:
class MyTestCase extends TestCase
{
use AssertJson;
public function setUp(): void
{
parent::setUp();
$this->setUpAssertJson();
}
public function text_example()
{
$this->get('api/users')->assertJsonStructureExact(['data' => ['*' => ['id', 'email']]]);
}
}
Here is my solution.
<?php namespace Tests\Support; use Illuminate\Foundation\Testing\TestResponse; use PHPUnit\Framework\Assert as PHPUnit; trait AssertJson { public function setUpAssertJson() { TestResponse::macro('assertJsonStructureExact', function (array $structure = null, $responseData = null) { if (is_null($structure)) { return $this->assertJson($this->json()); } if (is_null($responseData)) { $responseData = $this->decodeResponseJson(); } if (! array_key_exists('*', $structure)) { $keys = array_map(function ($value, $key) { return is_array($value) ? $key : $value; }, $structure, array_keys($structure)); PHPUnit::assertEquals($keys, array_keys($responseData)); } foreach ($structure as $key => $value) { if (is_array($value) && $key === '*') { PHPUnit::assertInternalType('array', $responseData); foreach ($responseData as $responseDataItem) { $this->assertJsonStructureExact($structure['*'], $responseDataItem); } } elseif (is_array($value)) { PHPUnit::assertArrayHasKey($key, $responseData); $this->assertJsonStructureExact($structure[$key], $responseData[$key]); } else { PHPUnit::assertArrayHasKey($value, $responseData); } } return $this; }); } }
Usage:
class MyTestCase extends TestCase { use AssertJson; public function setUp(): void { parent::setUp(); $this->setUpAssertJson(); } public function text_example() { $this->get('api/users')->assertJsonStructureExact(['data' => ['*' => ['id', 'email']]]); } }
Maybe, this post is closed and old, but I want shared the updated code for future users:
<?php
namespace Tests\Support;
use Illuminate\Foundation\Testing\TestResponse;
use Illuminate\Support\Arr;
use PHPUnit\Framework\Assert as PHPUnit;
trait AssertJson
{
public function setUpAssertJson(): void
{
TestResponse::macro('assertJsonStructureExact', function (array $structure = null, $responseData = null) {
if ($structure === null) {
return $this->assertJson($this->json());
}
if ($responseData === null) {
$responseData = $this->decodeResponseJson();
}
if (!array_key_exists('*', $structure)) {
$keys = array_map(static function ($value, $key) {
return is_array($value) ? $key : $value;
}, $structure, array_keys($structure));
PHPUnit::assertEquals(Arr::sortRecursive($keys), Arr::sortRecursive(array_keys($responseData)));
}
foreach ($structure as $key => $value) {
if (is_array($value) && $key === '*') {
PHPUnit::assertIsArray($responseData);
foreach ($responseData as $responseDataItem) {
$this->assertJsonStructureExact($structure['*'], $responseDataItem);
}
} elseif (is_array($value)) {
PHPUnit::assertArrayHasKey($key, $responseData);
$this->assertJsonStructureExact($structure[$key], $responseData[$key]);
} else {
PHPUnit::assertArrayHasKey($value, $responseData);
}
}
return $this;
});
}
}
If you're using Laravel 7, TestResponse
has moved. The new namespace is Illuminate\Testing\TestResponse
.
You can actually move the setup function into the trait itself.
I create a gist based on the example for anyone wanting to grab the code
https://gist.github.com/Oldenborg/dbea4eb6df6cabf388310e6b0168262f
Most helpful comment
Here is my solution.
Usage: