Ben
Ben

Reputation: 270

Integration Testing JSON API Response

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

Answers (1)

helmbert
helmbert

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

Related Questions