Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
The English summary is being prepared. Showing the original description.
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.
The English summary is being prepared. Showing the original description.
See how to installReview the instructions the agent will receive before you install.
Use this skill when writing or reviewing API endpoint tests — integration tests, contract validation, response structure checks, or external service mocking.
Http::fake().test-case-discovery funnel 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 three broken-access-control negative tests are mandatory).assertJsonStructure().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 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']);
});
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']);
});
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();
});
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);
});
// 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');
// 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
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'
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');
});
| 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 |
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.
Http::fake() — never hit real services in tests.Http::fake() without also testing the real integration path.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.
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.
The examples above are Laravel's. The shape they teach — assert the status,
then the body contract, then the side effect, and keep one behavior per test —
transfers unchanged; the API does not. Resolve the runner with
resolve_toolchain and read its ecosystems:
ecosystems | Request + assertion surface |
|---|---|
php | $this->getJson(...) / assertStatus / assertJsonPath |
js | supertest or the framework's own test client, with expect(res.status) |
python | httpx.AsyncClient / framework TestClient; assert res.status_code |
go | httptest.NewServer + net/http, or the router's own ServeHTTP |
Whatever the surface, the negative tests are not optional: unauthenticated,
not-owner and cross-tenant each get their own case, per broken-access-control.
No reviews yet. Tell others what you thought.
No login needed. Share how it worked for you, ask a question, or note where it did not run.
No comments yet. Be the first to write one.
Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
The English summary is being prepared. Showing the original description.
Use when defining or auditing the activation event — aha-moment selection, retention correlation, falsifiable definition. Triggers on 'what is our aha moment', 'redefine activation'.
The English summary is being prepared. Showing the original description.
Use when capturing an architectural decision — file naming, next ADR number, Status / Context / Decision / Consequences, index regen; fires even without saying 'ADR'.
The English summary is being prepared. Showing the original description.
Adversarial critique — devil's advocate, stress-test, honest teardown ('poke holes', 'be brutal', 'was hältst du davon'); explicit request only. Routine code or design review → code-review.
The English summary is being prepared. Showing the original description.
Use when reading, creating, or updating agent documentation, module docs, roadmaps, or AGENTS.md. Understands the full .augment/, agents/, and copilot-instructions structure.
The English summary is being prepared. Showing the original description.
Use for an adversarial red-team / blue-team / auditor review of an AI agent's CONFIG + behaviour (rules, skills, MCP, hooks, permissions) — attack-chain → defensive-gap list, not a code audit.
The English summary is being prepared. Showing the original description.