Improve JSON:API test failure messages to include errors when data is expected

Created on 15 April 2024, 6 months ago
Updated 18 May 2024, 5 months ago

Problem/Motivation

A lot of times when JSON:API test fail there is an error which is not communicated to the developer in a helpfull way. This makes contributing and debugging very hard. The error message when something is wrong a lot of the times is:

1) Drupal\Tests\jsonapi\Functional\ShortcutTest::testCollectionFilterAccess
Undefined array key "data"

This means the test assume $document['data'], but cannot find it. When inpecting the resulting document one can actually find the error in the response. eg:

Array
        (
            [title] => Forbidden
            [status] => 403
            [detail] => The current user is not authorized to filter by the `spotlight` field, given in the path `spotlight`.
            [links] => ...
        )

This is helpfull information on what went wrong.

Steps to reproduce

Run a test that erros, it will give the unhelpfull 'Undefined array key "data"' if a test asserts data but cannot find is.

Proposed resolution

Add a method for parsing the request body. A lot of our code looks like this $doc = Json::decode((string) $response->getBody());.

  /**
   * @param \Psr\Http\Message\ResponseInterface $response
   *   Response to extract JSON:API document from.
   * @param bool $dataRequired
   *   Validate the data property is available in the response.
   *
   * @return ?array
   *   JSON:API document extracted from the response.
   */
  protected function getDocumentFromResponse(ResponseInterface $response, bool $dataRequired = TRUE): ?array {
    assert($this instanceof BrowserTestBase);

    $document = Json::decode((string) $response->getBody());

    if ($dataRequired === TRUE && !isset($document['data'])) {
      if (isset($document['errors'])) {
        $errors = [];
        foreach ($document['errors'] as $error) {
          $errors[] = $error['title'] . ' (' . $error['status'] . '): ' . $error['detail'];
        }
        $this->fail('Missing expected data property in document. Error(s): ' . PHP_EOL . '  ' . implode('  ' . PHP_EOL, $errors));
      }
      $this->fail('Missing expected data property in document but no errors found. Response body: ' . PHP_EOL . '  ' . $response->getBody());
    }
    return $document;
  }

So this would replace:

$doc = Json::decode((string) $response->getBody());

With:

$doc = $this->getDocumentFromResponse($response);

If one expects errors you can turn the validation off. Another option would be to just parse the reponse normally. I considered allowing to specify what key it validated, but that seems overkill.

// Documents expecting a reponse that is not valid. Not by default since most calls require 
// the data key. Sometimes you want to validate errors, but that is less frequent.
$docWithErrors = $this->getDocumentFromResponse($response, FALSE);

Remaining tasks

  1. Add a change record

User interface changes

n.a.

API changes

Adds a new trait that is used in functional tests in the jsonapi module.

Data model changes

n.a.

Release notes snippet

JSON:API tests expecting a document with data now verify the existence on the data attribute and give usefull errors when they fail. use this in test by using the getDocumentFromResponse method in the GetDocumentFromRequestTrait.

📌 Task
Status

Fixed

Version

10.3

Component
JSON API 

Last updated 3 days ago

Created by

🇳🇱Netherlands bbrala Netherlands

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.71.5 2024