'Laravel Collections. Is there some kind of assertStructure method?
I'm writing tests and I want to assert, that a returned collection has some specific structure.
For asserting json
s I'm using assertJsonStructure()
method on the Responce
object.
I have not found similar for the \Illuminate\Support\Collection
. Did I miss some package/framework method.
An example of what do I want
$collection = collect([
'name' => 'John',
'surname' => 'Smith',
'birthoday' => [
'day' => 23,
'month' => 5,
'year' => 1970,
],
]);
$collection->assertStructure([ //true
'name',
'surname',
'birthday' => ['day', 'month', 'year'],
]);
I will accept
no
as an answer too, but if it is with an example of how to validate such a nested collection.
Solution 1:[1]
There is no such function on Collection instance, the closest you can do are:
- check if it has a key with
has()
- Check if it contains some value with
contains()
- There are other methods to check if something exist but
If you need inspiration, you can get it with the way Laravel implements assertJsonStructure()
in /Illuminate/Foundation/Testing/TestResponse.php
:
/**
* Assert that the response has a given JSON structure.
*
* @param array|null $structure
* @param array|null $responseData
* @return $this
*/
public function assertJsonStructure(array $structure = null, $responseData = null)
{
if (is_null($structure)) {
return $this->assertJson($this->json());
}
if (is_null($responseData)) {
$responseData = $this->decodeResponseJson();
}
foreach ($structure as $key => $value) {
if (is_array($value) && $key === '*') {
PHPUnit::assertInternalType('array', $responseData);
foreach ($responseData as $responseDataItem) {
$this->assertJsonStructure($structure['*'], $responseDataItem);
}
} elseif (is_array($value)) {
PHPUnit::assertArrayHasKey($key, $responseData);
$this->assertJsonStructure($structure[$key], $responseData[$key]);
} else {
PHPUnit::assertArrayHasKey($value, $responseData);
}
}
return $this;
}
As you can see there is a recursive calls to check the structure in case there is sub-structure.
UPDATE:
As a basic test to solve your question, I modified the assertJsonStructure()
to have assertArrayStructure()
and this working test:
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$collect = collect(['name' => '1', 'detail' => ['age' => 1,'class' => 'abc']]);
$this->assertArrayStructure(['name', 'detail' => ['class', 'age']], $collect->toArray());
}
/**
* Assert the array has a given structure.
*
* @param array $structure
* @param array $arrayData
* @return $this
*/
public function assertArrayStructure(array $structure, array $arrayData)
{
foreach ($structure as $key => $value) {
if (is_array($value) && $key === '*') {
$this->assertInternalType('array', $arrayData);
foreach ($arrayData as $arrayDataItem) {
$this->assertArrayStructure($structure['*'], $arrayDataItem);
}
} elseif (is_array($value)) {
$this->assertArrayHasKey($key, $arrayData);
$this->assertArrayStructure($structure[$key], $arrayData[$key]);
} else {
$this->assertArrayHasKey($value, $arrayData);
}
}
return $this;
}
Solution 2:[2]
The answer is no, you can simply look for assertive laravel methods at the API documentation, there is no method at the namespace Illuminate\Support\Collection
which makes what you are looking for. (You can find Laravel assertive method here)
As a viable alternative, why you don't just serialize your collection and check it with the assertJsonStructure()
method?
You could use the response()
helper to populate a Illuminate/Foundation/Testing/TestResponse
:
use Illuminate/Foundation/Testing/TestResponse;
$testResponse = new TestResponse(response()->json($collection->toArray());
return $testResponse->assertJsonStructure([
'name',
'surname',
'birthday' => ['day', 'month', 'year'],
]);
How I came to this solution:
- You need the exact same object that returns the method
$this->json()
in a test, which comes from the traitMakesHttpRequests
right here. - As the method comment specifies, it returns a
Illuminate\Foundation\Testing\TestResponse
. - Look for the constructor at the docs and see what it needs, a generic response object,
Illuminate/Http/Response
.
Hope this helps you.
Solution 3:[3]
As of Laravel 8.x, the other answers are somewhat outdated. You can use AssertableJsonString
as TestResponse
uses that under the hood:
(new \Illuminate\Testing\AssertableJsonString($yourJson))->assertStructure($yourStructure);
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | |
Solution 3 | shaedrich |