Reputation: 270
I'm currently writing some tests for my API, and I'm curious to know if there is a better way to deal with this as I feel like this is the "hacky" way of doing things.
Code example below:
public function testListingOfAllUsers()
{
$users = $this->createUsers();
$client = $this->createClient();
$client->request("GET", "/users/");
$response = $client->getResponse();
$content = $response->getContent();
$decodedContent = json_decode($content);
$this->assertTrue($response->isOk());
$this->assertInternalType("array", $decodedContent->data);
$this->assertCount(count($users), $decodedContent->data);
foreach ($decodedContent->data as $data) {
$this->assertObjectHasAttribute("attributes", $data);
$this->assertEquals("users", $data->type);
}
}
I'm wondering if there's something better I can do to test my API matches the JSON API specification. Enlighten me! I'm pretty certain PHPUnit isn't my answer here.
Upvotes: 1
Views: 1252
Reputation: 38034
First of all, I don't believe that programatically asserting a certain JSON structure as you're doing right now is bad practice per se. However, I do agree that it might get cumbersome at some point and could be solved more efficiently.
I was having the same issue a while ago and ended up writing a new Composer package (helmich/phpunit-json-assert
, which is available as open source) that uses JSON schemata and JSONPath expressions for verifying the structure of a given JSON document.
Using a JSON schema, your example test case could be written as follows:
public function testListingOfAllUsers()
{
$users = $this->createUsers();
$client = $this->createClient();
$client->request("GET", "/users/");
$response = $client->getResponse();
$content = $response->getContent();
$decodedContent = json_decode($content);
$this->assertTrue($response->isOk());
$this->assertJsonDocumentMatchesSchema($decodedContent, [
'type' => 'array',
'items' => [
'type' => 'object',
'required' => ['attributes', 'type'],
'properties' => [
'attributes' => ['type' => 'object'],
'type' => ['type' => 'string', 'enum' => ['user']]
]
]
]);
}
Although a little more verbose (with regards to lines-of-code), I've come to appreciate JSON schemas for this use case, as it's a widely adopted standard and (imho) easier to read that a wall of assert*
statements. You could also extract the schema definitions from your unit tests into separate files and do other stuff with them; for example auto-generating documentation (Swagger also uses a subset of JSON schema) or run-time validation.
Upvotes: 3