plugins/vibeworks-library/skills/testing-strategy/SKILL.md
Test pyramid and testing strategy — unit, integration, and end-to-end test ratios, mocking strategies, test isolation, and what to test. Reference when planning test coverage or evaluating test quality.
npx skillsauth add claude-code-community-ireland/claude-code-resources testing-strategyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
/ E2E \ ~10% — Slow, brittle, expensive
/ Integ \ ~20% — Medium speed, real dependencies
/ Unit \ ~70% — Fast, isolated, numerous
/______________\
| Layer | Count | Speed | Scope | Runs | |-------------|--------|-----------|------------------------------|----------------| | Unit | ~70% | < 10ms | Single function or class | Every commit | | Integration | ~20% | < 5s | Multiple components together | Every push | | E2E | ~10% | < 60s | Full user workflow | Pre-merge, CI |
If the pyramid inverts (more E2E than unit), treat it as a structural problem.
| Property | Rule | |---------------------|------------------------------------------------------------| | Fast | Entire unit suite runs in under 30 seconds | | Isolated | No test depends on another test's execution or state | | Repeatable | Same result every run, regardless of time, network, or OS | | Self-validating | Pass or fail with no manual inspection needed | | Timely | Written before or alongside the production code |
describe("calculateDiscount", () => {
it("applies 10% discount for orders over $100", () => {
// Arrange
const order = { items: [{ price: 120, quantity: 1 }] };
// Act
const result = calculateDiscount(order);
// Assert
expect(result.discount).toBe(12);
expect(result.total).toBe(108);
});
});
class TestCalculateDiscount:
def test_applies_10_percent_for_orders_over_100(self):
order = Order(items=[Item(price=120, quantity=1)])
result = calculate_discount(order)
assert result.discount == 12
assert result.total == 108
| Category | Example | |---------------------|-------------------------------------------------------| | Database queries | Repository methods with a real (test) database | | API endpoints | HTTP request through middleware, handler, and response | | External services | Calls to third-party APIs with sandboxed accounts | | Message queues | Publish and consume cycle with real broker | | File system | Read/write operations with temp directories | | Cache layers | Cache set, get, invalidation with real cache |
describe("UserRepository", () => {
let db: Database;
beforeAll(async () => { db = await createTestDatabase(); await db.migrate(); });
afterAll(async () => { await db.close(); });
beforeEach(async () => { await db.truncateAll(); });
it("finds user by email", async () => {
await db.insert("users", { email: "[email protected]", name: "Test User" });
const user = await userRepo.findByEmail("[email protected]");
expect(user).not.toBeNull();
expect(user.name).toBe("Test User");
});
});
Include: Critical revenue paths (signup, checkout, payment), authentication flows, core user journeys (3-5 things your product must do), cross-system workflows.
Exclude: Edge cases (cover in unit tests), UI-only validation, admin features, scenarios covered by lower-level tests.
test("user can complete checkout", async ({ page }) => {
await page.goto("/login");
await page.fill('[name="email"]', "[email protected]");
await page.fill('[name="password"]', "password123");
await page.click('button[type="submit"]');
await page.goto("/products/widget-1");
await page.click("text=Add to Cart");
await page.goto("/cart");
await page.click("text=Checkout");
await page.fill('[name="card"]', "4242424242424242");
await page.click("text=Place Order");
await expect(page.locator(".order-confirmation")).toBeVisible();
});
| Mock | Reason | |---------------------------------|-----------------------------------------------| | External HTTP APIs | Avoid network dependency and rate limits | | Time and date functions | Enable deterministic time-based tests | | Random number generators | Enable reproducible outputs | | Email/SMS/notification services | Prevent sending real messages | | Payment processors | Avoid real charges during tests |
| Do Not Mock | Reason | |----------------------------|-----------------------------------------------------| | The module under test | You would be testing your mock, not your code | | Simple value objects | No external dependency, no side effects | | Standard library functions | Trusted, deterministic, fast | | Database in integration | The database interaction is what you are testing | | Everything by default | Over-mocking makes tests pass even when code breaks |
// Good: mock the HTTP client, not the service
const mockHttpClient = { get: jest.fn().mockResolvedValue({ data: { id: 1, name: "Test" } }) };
const service = new UserService(mockHttpClient);
// Bad: mocking the method you are testing
jest.spyOn(service, "getUser").mockResolvedValue({ id: 1 });
Prefer factories over shared fixtures to keep tests independent.
function buildUser(overrides: Partial<User> = {}): User {
return {
id: randomUUID(),
email: `user-${Date.now()}@test.com`,
name: "Test User",
role: "member",
createdAt: new Date(),
...overrides,
};
}
const admin = buildUser({ role: "admin" });
Test invariants with randomly generated inputs.
import fc from "fast-check";
test("sort is idempotent", () => {
fc.assert(fc.property(fc.array(fc.integer()), (arr) => {
expect(sort(sort(arr))).toEqual(sort(arr));
}));
});
test("encode/decode roundtrip", () => {
fc.assert(fc.property(fc.string(), (input) => {
expect(decode(encode(input))).toBe(input);
}));
});
Use for: serialization roundtrips, sorting invariants, mathematical properties, parsers, encoding.
Appropriate: Serialized output formats, generated config files, stable component render output. Harmful: Large frequently-changing outputs, UI with dynamic content, testing logic or behavior.
| Metric | Target | Notes | |---------------------|---------|----------------------------------------------| | Line coverage | >= 80% | Reasonable baseline for most projects | | Branch coverage | >= 75% | More meaningful than line coverage | | Critical path | >= 95% | Auth, payments, data integrity | | New code coverage | >= 90% | Enforce on PRs with coverage diff tools |
| Anti-Pattern | Problem | Fix | |--------------------------|--------------------------------------------------|-------------------------------------------| | Testing implementation | Breaks when refactoring, even if behavior is same | Test behavior and outputs | | Flaky tests | Erodes trust, gets ignored | Fix or delete immediately | | Slow test suite | Developers skip running tests | Parallelize, mock I/O, split by layer | | Over-mocking | Tests pass but code is broken | Mock boundaries only | | Shared mutable state | Tests pass alone, fail together | Isolate state per test, use factories | | Testing private methods | Couples tests to implementation details | Test through the public API | | No assertion | Test passes without verifying anything | Every test must assert something | | Ignoring test failures | Hides real bugs | Fix or remove; never skip permanently |
// describe/it style
describe("ShoppingCart")
it("calculates total with tax for items in cart")
// given/when/then style
test("given an empty cart, when adding an item, then total reflects item price")
// should style
test("should return 404 when user is not found")
tools
Automate changelog generation from commits, PRs, and releases following Keep a Changelog format. Use when setting up release workflows, generating release notes, or standardizing commit conventions.
documentation
Project Guidelines Skill (Example)
development
Development skill from everything-claude-code
documentation
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.