Universal test-driven development methodology and test design craft. Background knowledge for writing behavior-focused tests, mocking strategy, vertical slicing, and tracer bullet execution. Auto-loaded when writing tests during implementation, review feedback, or standalone test authoring. Triggers: writing tests, test design, mocking strategy, test quality, behavior testing, integration testing, test-first, red-green-refactor.
From engnpx claudepluginhub inkeep/team-skills --plugin engThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Optimizes cloud costs on AWS, Azure, GCP via rightsizing, tagging strategies, reserved instances, spot usage, and spending analysis. Use for expense reduction and governance.
Universal principles for writing good tests. These apply regardless of framework, language, or repo — discover and follow your repo's specific testing tools, commands, and conventions from its project configuration, contributor guides, existing test files, CI/CD config, and any repo-level AI skills or rules.
Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't break unless behavior changed.
Good tests exercise real code paths through public APIs. They describe what the system does, not how. A good test reads like a specification — "user can checkout with valid cart" tells you exactly what capability exists. These tests survive refactors because they don't care about internal structure.
Bad tests are coupled to implementation. They mock internal collaborators, test private methods, or verify through external means (like querying a database directly instead of using the interface). The warning sign: your test breaks when you refactor, but behavior hasn't changed.
// GOOD: Tests observable behavior through the interface
test("created user is retrievable", async () => {
const user = await createUser({ name: "Alice" });
const retrieved = await getUser(user.id);
expect(retrieved.name).toBe("Alice");
});
// BAD: Bypasses interface to verify via implementation detail
test("createUser saves to database", async () => {
await createUser({ name: "Alice" });
const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
expect(row).toBeDefined();
});
// GOOD: Tests the outcome
test("user can checkout with valid cart", async () => {
const cart = createCart();
cart.add(product);
const result = await checkout(cart, paymentMethod);
expect(result.status).toBe("confirmed");
});
// BAD: Tests the mechanism
test("checkout calls paymentService.process", async () => {
const mockPayment = vi.spyOn(paymentService, "process");
await checkout(cart, payment);
expect(mockPayment).toHaveBeenCalledWith(cart.total);
});
Red flags for implementation-coupled tests:
Mock at system boundaries only:
Do not mock your own modules, internal collaborators, or anything you control. If you need to mock an internal module to test something, that's a signal the interface design needs work — not that you need more mocks.
Design for mockability at boundaries:
// Easy to mock — dependency is injected
function processPayment(order, paymentClient) {
return paymentClient.charge(order.total);
}
// Hard to mock — dependency is internal
function processPayment(order) {
const client = new StripeClient(process.env.STRIPE_KEY);
return client.charge(order.total);
}
DO NOT write all tests first, then all implementation. This is "horizontal slicing" — it produces weak tests:
Correct approach — vertical slices via tracer bullets:
WRONG (horizontal):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
RIGHT (vertical):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
Each test responds to what you learned from the previous cycle. Because you just wrote the code, you know exactly what behavior matters and how to verify it.
Start with ONE test that proves ONE path works end-to-end:
RED: Write test for first behavior → test fails
GREEN: Write minimal code to pass → test passes
This is your tracer bullet — it proves the path works before you invest in breadth. Then add behaviors incrementally, one test at a time.
Rules:
[ ] Test describes behavior, not implementation
[ ] Test uses public interface only
[ ] Test would survive an internal refactor
[ ] Code is minimal for this test
[ ] No speculative features added