nothingworksinc / ticketbeast

Back to the lesson videos:
https://course.testdrivenlaravel.com/lessons
552 stars 11 forks source link

API Response Assertion Macros (for ya'll) #87

Open zlanich opened 6 years ago

zlanich commented 6 years ago

Hey all! After going through this course, I still had issues with asserting API JSON responses in an expressive way given the way Laravel's TestResponse helpers work, so I created a few macros that helped, so I'm posting them here if anyone wants to use them. Feel free to suggest improvements 😄

My main issues where:

  1. $this->assertJsonMissing(), etc doesn't work with nested keys - ie. Laravel's ['data' => '...'] convention using Resource classes. There are some issues on this, but Taylor chose to leave this out of core.
  2. Asserting sort orders, etc in an expressive way, especially given # 1 above

Example Usage for a Geo-Location App:

// Not Nearby
factory(Some::class)->create(['location' => '40,-80.1111', 'some-identifier' => 1003]);

// Nearby - But created out of order distance wise (intentionally)
factory(Some::class)->create(['location' => '40,-80.0002', 'some-identifier' => 1001]);
factory(Some::class)->create(['location' => '40,-80.0003', 'some-identifier' => 1002]);
factory(Some::class)->create(['location' => '40,-80.0001', 'some-identifier' => 1000]);
$userLocation = '40,-80.0000'; // lat, long

$response = $this->json('GET', "/locations?near={$userLocation}");

$response->assertHasWhere('some-identifier', [1000, 1001, 1002]);
$response->assertMissingWhere('some-identifier', [1003]);
$response->assertSort('some-identifier', [1000, 1001, 1002]);

Macros:

/**
 * Asserts response has objects with the given key/value(s)
 */
TestResponse::macro('assertHasWhere', function (string $key, $values, bool $strict = false)
{
    $content = $this->decodeResponseJson();
    $content = $content['data'] ?? $content;

    $values = is_array($values) ? $values : [$values];

    $method = $strict ? 'whereInStrict' : 'whereIn';
    PHPUnit::assertTrue((collect($content)->$method($key, $values)->count() === sizeof($values)), 'Failed to assert json response contained all values');

    return $this;
});

/**
 * Asserts response has objects sorted in the specified order by key
 */
TestResponse::macro('assertSort', function (string $key, array $values, bool $strict = false)
{
    $content = $this->decodeResponseJson();
    $content = $content['data'] ?? $content;

    $method = $strict ? 'whereInStrict' : 'whereIn';
    $contentSubset = collect($content)->$method($key, $values)->pluck($key)->all();
    PHPUnit::assertEquals($values, $contentSubset, 'Failed to assert sorted');

    return $this;
});

/**
 * Asserts response is missing objects with the given key/value(s)
 */
TestResponse::macro('assertMissingWhere', function (string $key, $values, bool $strict = false)
{
    $content = $this->decodeResponseJson();
    $content = $content['data'] ?? $content;

    $values = is_array($values) ? $values : [$values];

    $method = $strict ? 'whereInStrict' : 'whereIn';
    PHPUnit::assertTrue((collect($content)->$method($key, $values)->count() === 0), 'Failed to assert json response was missing all values');

    return $this;
});

I toyed with just added an $assertSort flag to assertHasWhere, along with a few other approaches, but the error messages were a bit convoluted, so I split them out into separate macros.

Enjoy!

P.S. I couldn't find a better place to post these, so if an Issue isn't the best place, let me know.