Api testing
Use when writing API endpoint tests — integration tests, contract validation, response assertions, mocked external services — even when the user says 'test this route' without naming API testing.From its SKILL.md
npx -y skills add event4u-app/agent-config --skill api-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.4 KB, ~1.6k tokens by cl100k_base, as published. Nobody here has run it
api-testing
When to use
Use this skill when writing or reviewing API endpoint tests — integration tests, contract validation, response structure checks, or external service mocking.
Procedure: Write API tests
- Understand the endpoint — Read the controller, form request, and existing tests. Understand expected behavior, edge cases, and auth requirements before writing anything.
- Set up test data — Use seeders (preferred) or factories. Mock external services with
Http::fake(). - Enumerate test cases — Run the
test-case-discoveryfunnel first; cover success, validation errors, authorization failures, and edge cases — floor per behavior: 1 happy + 1 boundary + 1 error (+1 abuse case; on data-returning endpoints the threebroken-access-controlnegative tests are mandatory). - Assert response — Check status code, JSON structure, data values. Use
assertJsonStructure(). - Verify — Run the test. Must pass. Check no flaky assertions (no time-dependent, no random ordering).
Example
describe('GET /api/v1/projects', function () {
it('returns paginated projects for authenticated user', function () {
$user = loginAsTestUser();
$response = $this->getJson('/api/v1/projects');
$response->assertOk()
->assertJsonStructure([
'data' => [['id', 'title', 'status']],
'meta' => ['current_page', 'per_page', 'total'],
]);
});
it('returns 401 for unauthenticated request', function () {
$this->getJson('/api/v1/projects')
->assertUnauthorized();
});
it('returns 403 when user lacks permission', function () {
loginAsRestrictedUser();
$this->getJson('/api/v1/projects')
->assertForbidden();
});
});
Test categories
Happy path
Test the expected success scenario with valid input:
it('creates a project', function () {
loginAsTestUser();
$this->postJson('/api/v1/projects', [
'title' => 'New Project',
'customer_id' => $customerId,
])
->assertCreated()
->assertJsonPath('data.title', 'New Project');
$this->assertDatabaseHas('projects', ['title' => 'New Project']);
});
Validation
Test that invalid input is rejected with correct error messages:
it('rejects project without title', function () {
loginAsTestUser();
$this->postJson('/api/v1/projects', [
'customer_id' => $customerId,
])
->assertUnprocessable()
->assertJsonValidationErrors(['title']);
});
Authorization
Test that unauthorized access is blocked:
it('prevents non-owner from updating project', function () {
$otherUser = loginAsOtherUser();
$this->putJson("/api/v1/projects/{$project->id}", [
'title' => 'Hijacked',
])
->assertForbidden();
});
Edge cases
Test boundary conditions:
it('handles empty collection', function () {
loginAsTestUser();
$this->getJson('/api/v1/projects')
->assertOk()
->assertJsonCount(0, 'data');
});
it('paginates large result sets', function () {
loginAsTestUser();
$this->getJson('/api/v1/projects?per_page=5')
->assertOk()
->assertJsonPath('meta.per_page', 5);
});
Response contract validation
Assert JSON structure
// Verify response shape (keys exist)
$response->assertJsonStructure([
'data' => ['id', 'title', 'status', 'created_at'],
]);
// Verify exact values
$response->assertJsonPath('data.status', 'active');
// Verify collection count
$response->assertJsonCount(3, 'data');
Assert response types
// When strict typing matters
$data = $response->json('data');
expect($data['id'])->toBeInt();
expect($data['title'])->toBeString();
expect($data['total'])->toBeString(); // Money as string, not float
Filter noisy responses
When a failing test dumps the full JSON body, narrow the diagnosis with jq or grep
instead of scrolling the whole payload:
# Extract only the failing assertion path
echo "$RESPONSE_JSON" | jq '.data.status, .errors'
# Targeted log scan
rg --json 'API call failed' storage/logs/laravel.log | jq -r '.data.lines.text'
External service mocking
it('handles external API failure gracefully', function () {
Http::fake([
'external-api.com/*' => Http::response(null, 500),
]);
loginAsTestUser();
$this->postJson('/api/v1/sync')
->assertStatus(502)
->assertJsonPath('message', 'External service unavailable');
});
Test checklist per endpoint
| Category | Tests needed |
|---|---|
| Auth | Unauthenticated (401), unauthorized (403) |
| Validation | Missing fields, wrong types, boundary values |
| Happy path | Success with valid input, correct status code |
| Response | JSON structure, field types, pagination meta |
| Side effects | Database changes, events dispatched, jobs queued |
| Edge cases | Empty results, large payloads, concurrent access |
Bridge to UI verification
API tests cover the contract layer. When an endpoint feeds a UI surface (Livewire component, Blade-rendered page, SPA route), complement the API test with a thin UI probe: a livewire test for wired components, or a Playwright spec / browser screenshot for the rendered shell. Never assume the UI works just because the API test is green.
Output format
- Test file in the project’s test framework (Pest, Jest, pytest) covering happy path, validation, auth, and edge cases
- Test names as readable sentences describing expected behavior
- Mocked external services where applicable
Auto-trigger keywords
- API test
- endpoint test
- integration test
- response validation
- contract testing
Gotcha
- Don't test framework internals (e.g., "does Laravel return 422 on validation error") — test YOUR validation rules.
- Always seed test data explicitly — don't rely on data from other tests (parallel execution).
- Mock external APIs with
Http::fake()— never hit real services in tests. - The model forgets to assert response structure, only checking status codes — always check both.
Do NOT
- Do not hardcode IDs or timestamps — use factories or seeders.
- Do not skip auth tests — always test both authenticated and unauthenticated.
- Do not assert entire JSON responses — assert only meaningful fields.
- Do not use
Http::fake()without also testing the real integration path.
Anti-bruteforce — diagnose before retry
When a test fails, do not retry blindly with tweaked assertions until something passes. Diagnose the root cause first: print the actual response shape once, compare it to the contract, then write a targeted fix. Trial-and-error retries hide real regressions.
Clarification guard — ambiguous contract → ask
If the endpoint contract is ambiguous (unclear status code, optional fields, error envelope shape), do not assume. Ask the user or check the OpenAPI spec / route definition before writing assertions — never guess the response shape from the route name.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most test skills give in ~1.6k tokens
Counted across 964 of the 1,571 authors here whose files we hold, read 2026-08-07
- Close the browser when donein 55 of 964, across 12 files
- Wait for network idle statein 51 of 964, across 6 files
- Launch Chromium in headless modein 49 of 964, across 6 files
- Use descriptive selectors for elementsin 49 of 964, across 6 files
- Run provided scripts with help flag firstin 49 of 964, across 6 files
- Add appropriate explicit waitsin 48 of 964, across 5 files
- Use bundled scripts as black boxesin 46 of 964, across 3 files
- Do not read script source codein 46 of 964, across 3 files
- Use sync playwright for scriptsin 46 of 964, across 3 files
- Inspect dom before executing actionsin 46 of 964, across 3 files
- Run the full test suitein 37 of 964
- Write the failing test firstin 29 of 964, across 23 files
Said here and by no other author read
- use seeders or factories for test data
- run test case discovery before writing tests
- assert status code, json structure, and data values
- verify tests pass without flaky assertions
- test custom validation rules
- always seed test data explicitly
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.