Fluent-перевірки дають змогу описати відповідь повністю: значення, типи, вкладені структури й відсутність зайвих полів.
use Illuminate\Testing\Fluent\AssertableJson;
$this->getJson('/api/vacancies?filter[city]=kyiv')
->assertOk()
->assertJson(fn (AssertableJson $json) => $json
->has('data', 3, fn (AssertableJson $vacancy) => $vacancy
->where('city', 'kyiv')
->whereType('id', 'integer')
->whereType('salary_from', 'integer|null')
->has('company', fn (AssertableJson $company) => $company
->hasAll(['id', 'name'])
->missing('owner_email')
->etc()
)
->etc()
)
->has('meta')
->has('links')
);
Основні методи:
where('key', $value)- точне значення; можна передати замикання для власної умови;whereType('key', 'string')- тип (string,integer,array,null, через|- кілька);has('key'),has('items', 3)- наявність і кількість;has('data', 3, fn ...)- кількість і перевірка першого елемента колекції;each(fn ...)- перевірка кожного елемента;hasAll([...]),hasAny([...]),missing('key'),missingAll([...]).
Найважливіше - строгість за замовчуванням. Кожен рівень, перевірений через замикання, вимагає, щоб усі ключі на цьому рівні були перевірені. Незгадане поле - провал тесту. etc() явно дозволяє «інші поля теж можуть бути».
Це захищає від головного ризику API: нове поле, що випадково потрапило у відповідь (наприклад, хтось додав модель цілком замість ресурсу, і в JSON з'явилися внутрішні поля). Без etc() тест це помітить.
Коли що обирати:
- простий тест одного значення -
assertJsonPath; - контракт ресурсу (усі поля й типи, нічого зайвого) - fluent-перевірки без
etc()на ключових рівнях; - дуже великі відповіді - перевірка відповідності специфікації OpenAPI (бібліотеки валідації відповідей за схемою), щоб не дублювати опис контракту в тестах і в документації.
Пастка: has('data', 3, ...) перевіряє замиканням лише перший елемент. Для перевірки всіх - has('data', 3) і окремо ->each(...) або ->has('data.0', ...)/'data.1' для конкретних позицій.