Testing

The toolkit provides enhanced response assertions designed for JSON:API responses. They let you write expressive, chainable tests with both Pest and PHPUnit.

Setup

Add the CreatesTestResponse trait to your tests/Pest.php:

uses(
    Tests\TestCase::class,
    BlueBeetle\ApiToolkit\Testing\CreatesTestResponse::class,
)->in('Feature');

Extend the toolkit's TestCase in your test classes:

use BlueBeetle\ApiToolkit\Testing\TestCase;

class ProductApiTest extends TestCase
{
    // ...
}

Or add the trait to your own TestCase:

use BlueBeetle\ApiToolkit\Testing\CreatesTestResponse;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesTestResponse;
}

This overrides createTestResponse() so all HTTP test responses (getJson, postJson, etc.) return the toolkit's TestResponse with JSON:API-specific assertions.

Response Assertions

Resource Type and ID

$response = $this->getJson('/api/products/1');

$response->assertOk();
$response->assertResourceType('products');
$response->assertResourceId('1');

Attributes

$response->attributes()
    ->assertSame('name', 'Widget Pro')
    ->assertSame('price', 29.99)
    ->assertNotNull('created_at');

Collection Data

$response = $this->getJson('/api/products');

$response->data()->assertCount(10);
$response->data()->item(0)->assertSame('type', 'products');
$response->data()->item(0)->attributes()->assertSame('name', 'Widget Pro');

Included Resources

$response = $this->getJson('/api/products/1?include=category');

$response->included()
    ->assertNotEmpty()
    ->item(0)
    ->assertSame('type', 'categories');

Meta

$response->meta()
    ->assertSame('currentPage', 1)
    ->assertSame('total', 50);

Error Assertions

Basic Error

$response = $this->getJson('/api/products/999');

$response->assertNotFound();
$response->assertErrorTitle('Not Found');
$response->assertErrorDetail('Product not found');

Validation Errors

$response = $this->postJson('/api/products', []);

$response->assertStatus(422);
$response->assertValidationError('name', 'The name field is required.');

Error Code and Detail

$response->assertErrorCode('INSUFFICIENT_FUNDS');
$response->assertErrorDetailContains('balance');

Chainable Data Navigation

The TestDataResponse class lets you navigate into nested structures:

$response->data()                    // -> data
    ->item(0)                        // -> data[0]
    ->attributes()                   // -> data[0].attributes
    ->assertSame('name', 'Widget');

$response->data()
    ->item(0)
    ->relationships()                // -> data[0].relationships
    ->assertHasKey('category');

$response->data()
    ->item(0)
    ->meta()                         // -> data[0].meta
    ->assertHasKey('stock_level');

Available Assertions

MethodDescription
assertSame(key, value)Strict equality
assertNotSame(key, value)Strict inequality
assertTrue(key)Value is true
assertFalse(key)Value is false
assertNull(key)Value is null
assertNotNull(key)Value is not null
assertHasKey(key)Key exists
assertMissingKey(key)Key does not exist
assertCount(int)Array has N items
assertEmpty()Array is empty
assertNotEmpty()Array is not empty

TestResponse Assertions

MethodDescription
assertResourceType(type)Assert data.type matches
assertResourceId(id)Assert data.id matches
assertErrorTitle(title)Assert first error title
assertErrorDetail(detail)Assert first error detail
assertErrorDetailContains(needle)Assert first error detail contains string
assertErrorCode(code)Assert first error code
assertValidationError(field, message)Assert validation error for field with message
MethodReturnsDescription
data()TestDataResponseAccess data key
attributes()TestDataResponseShortcut for data.attributes
errors()TestDataResponseAccess errors array
included()TestDataResponseAccess included array
meta()TestDataResponseAccess meta object
item(index)TestDataResponseScope to array item by index
relationships()TestDataResponseScope to relationships key
links()TestDataResponseScope to links key