Asserting API Response

Automation tests are only validating if they assert the expected results. Asserts are used to verify the expected results from the endpoint.

Status Code Asserts:

Status code asserts ensures that the call to the endpoint returns a specific status code when returned.

The most commonly used will be:

->assertOk()

->assertCreated()

->assertNotFound()

->assertNoContent()

->assertStatus()

Laravel Documentation for all available assertions

Example:

/**
 * Test that the route /user/mobilenumber returns 200
 *
 * 'number' => 'required|min:10|max:10',
 * 
 * @test
 * @return void
 */
public function user_put_method_mobile_number_returns_200(): void
{
    $this->actingAsAdmin()
        ->authenticate()
        ->put('/user/mobilenumber', [
            "number" => "1555".rand(100000,999999)
        ])
        ->assertOk();
}

An alternative way of testing the status code is by using ->assertStatus(X) , where X is the expected status code value e.g. ->assertStatus(200).

JSON Response Asserts:

When the request has returned with a response during the functional tests, an assert should be used to determine if the correct response has returned. Add a message to an assert when possible to increase the maintainability of the tests.

Using ->assertExactJson , the test can compare the response to the exact expected response. The test will fail if there is a difference, and a message within the terminal will print the discrepancy.

Example:

/**
 * Test that the route /user/mobilenumber returns 200
 *
 * 'number' => 'required|min:10|max:10',
 * 
 * @test
 * @return void
 */
public function user_put_method_mobile_number_returns_200(): void
{
    $randomNumber = "1555".rand(100000,999999);
    $this->actingAsAdmin()
         ->authenticate()
         ->put('/user/mobilenumber', [
            "number" => $randomNumber
         ])
         ->assertOk()
         ->assertExactJson([
             'status' => 'success',
             'mobile_number_verified' => false,
             'message' => 'The phone number has been updated successfully to ' . $randomNumber . '.',
         ]);
}

Note that this example is using assertExactJson(), ensure that the JSON returned by the API matches the array exactly. If a single property is absent, the assertion will fail.

If it is undesirable to assert the entire JSON response, then use ->assertJsonFragment()

->assertJsonFragment(
  [
      'event_title' => 'Test Event 1',
      'course_id' => 1,
      'event_duration' => 60,
  ],
  [
      'event_title' => 'Test Event 3',
      'course_id' => 4,
      'event_duration' => 60,
  ]
);

Fluent JSON Testing

Laravel offers an alternative way to assert JSON responses using the AssertableJson utility. For example, when a more complex assertion is needed.

For more info about fluent JSON assertions, check out the official documentation.

$this->actingAsAdmin()
      ->authenticate()
      ->put('/user/mobilenumber', [
            "number" => $randomNumber
      ])
      ->assertOk()
      ->assertJson(fn(AssertableJson $json) => 
            $json->where("status", "success")->has("message")->etc()
      );

AssertableJson asserts

Here are some examples of fluent assertions

->count()
->assertJson(fn (AssertableJson $json) => $json
    ->count(5) // Validate the number of expected events
)

We can assert to see how many elements have been returned in a JSON response.

->each()
->each(fn ($json) => $json->count(4))

This will run the anonymous function on each element in the response.

->whereAllType([]) / ->whereType()
->whereAllType([
    'event_id' => 'integer',
    'event_title' => 'string',
    'cunit_id' => 'integer|null', // optional value
    'course_id' => 'integer',
    'event_start' => 'string',
    'event_finish' => 'string',
    'event_duration' => 'integer',
])
->whereType('audience_types', 'array');

The whereAllType assertion checks if the data type returned from the endpoint is correct. Adding a vertical bar | acts as an or if the value can be a mixed value type. Adding null specifies that it’s an optional value returned.

When there is a delay between when the request is received and the response is returned by the server, do not use the current timestamp time() directly in JSON assertions. Instead, save the value of the timestamp in a variable prior to making the request so that it may be asserted against later.

See Laravel API Docs for more advanced fluent methods: Laravel API Documentation

Testing File Uploads

Laravel simplifies testing file uploads using the Illuminate\Http\UploadedFile::fake() method. For example, when testing a CSV file upload, do the following:

$data = implode("\n", [
    'Number/Email,First Name,Last Name',
    'developer2@elentra.com,Jane,student',
]);

$response = $this->actingAsAdmin()
    ->authenticate()
    ->post('clinical/schedules/3/audience/csv', [
        'file' => UploadedFile::fake()->createWithContent('foo.csv', $data)
    ])
    ->assertOk()
    ->assertJson( /** ... */);

Check the official documentation for information on testing file uploading.

Interacted

Verifying that the test has interacted with all the properties of the JSON response will ensure that updated endpoints not reflected in the test will be caught. The assertion ->interacted() can be used:

->whereAll([
    'event_id' => 2, // Second will be first since the start date is the earliest.
    'event_title' => 'Test Event at Fixed Time',
    'cunit_id' => null,
    'course_id' => 1,
    'event_start' => '1644974420',
    'event_finish' => '1644984420',
    'event_duration' => 60,
])
->interacted();

See and Don't See Asserts

->assertDontSeeText("Cases/Week_DELETED")
->assertDontSeeText("DELETED_CASE")
->assertDontSeeText("Deleted_Unit", false)
->assertSeeText("Case Created with week in Published");

These are very easy assertions to see if a specific search string can be found. This can help ensure that deleted entities are not being returned. It is recommended to save data with titles like Deleted_ so that assertions do not misidentify similarly worded entities.

What and what not to assert

When asserting the contents of a JSON response from an HTTP request, only test specific values that are not likely to change.

The ID field from a POST request will likely change as auto-increment values for primary keys may differ.

->asertJson(fn (AssertableJson $json) =>
     $json→whereType('id', 'integer')
)

In GET requests, especially if the seed data specified the same ID for inserting a record, it can be helpful to test if the ID is precisely as expected to ensure the correct records are retrieved.

->asertJson(fn (AssertableJson $json) =>
     $json→where('id', 3)
)

The created date, updated date, and updated by are likely to change. Avoid checking for exact values.

->asertJson(fn (AssertableJson $json) =>
     $json→whereType('created_date', 'integer')
          →whereType('updated_date', 'integer|null')
          →whereType('deleted_date', 'integer|null')
)

Use the ->has() assertion to check if the response contains the correct number of elements.

->asertJson(fn (AssertableJson $json) =>
     $json→has('users', 3)
)

Incomplete Tests

Mark a test as incomplete for either an unimplemented method or a test that can’t be fully asserted for a specific reason.

$this->markTestIncomplete('This test has not been implemented yet.');

markTestIncomplete should be added to the top of the test method with the context of why the test is incomplete. This prevents the execution of any assertions that follow in the test method.

Last updated