testing-patterns
Testing patterns and principles. Unit, integration, mocking strategies.
Install
mkdir -p .claude/skills/testing-patterns-harmitx7 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13860" && unzip -o skill.zip -d .claude/skills/testing-patterns-harmitx7 && rm skill.zipInstalls to .claude/skills/testing-patterns-harmitx7
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Testing patterns and principles. Unit, integration, mocking strategies.About this skill
Testing Patterns
Tests don't prove code works. They make it safe to change. A codebase without tests is a codebase you're afraid to touch.
Test Pyramid
Write tests at the right level. Most tests should be unit tests. Fewer integration tests. Fewest E2E tests.
/\
/E2E\ Fewest — expensive to write and run
/------\
/Integr. \ Some — verify component interactions
/----------\
/ Unit Tests \ Most — fast, isolated, focused
/--------------\
Why this shape:
- Unit tests run in milliseconds — you can have thousands
- E2E tests take seconds — you want dozens, not hundreds
- Inverting the pyramid = slow CI, fragile test suite, low confidence
AAA Pattern
Every test follows this structure:
it('should return 401 when token is expired', async () => {
// Arrange — set up the scenario
const expiredToken = generateToken({ expiresIn: '-1s' });
const request = buildRequest({ authorization: `Bearer ${expiredToken}` });
// Act — do the thing being tested
const response = await authMiddleware(request);
// Assert — verify the outcome
expect(response.status).toBe(401);
expect(response.body.error).toBe('Token expired');
});
Never combine Arrange and Assert. Never skip Arrange by relying on test state from a previous test.
Unit Tests
Unit tests test one unit of logic in isolation. Everything external is replaced with a controlled substitute.
// Test the function's logic — not the database, not the network
it('should hash the password before saving', async () => {
const mockSave = vi.fn().mockResolvedValue({ id: '1' });
const userRepo = { save: mockSave };
await createUser({ email: '[email protected]', password: 'secret' }, userRepo);
const savedUser = mockSave.mock.calls[0][0];
expect(savedUser.password).not.toBe('secret'); // was hashed
expect(savedUser.password).toMatch(/^\$2b\$/); // bcrypt format
});
Rules for unit tests:
- One assertion per concept (multiple
expectcalls are fine if they verify the same behavior) - No network calls, no file system, no real database
- Tests are order-independent — they don't rely on state from other tests
- Test names describe behavior:
should X when Yorreturns X given Y
Integration Tests
Integration tests verify that two or more components work together correctly.
// Tests the real database interaction
it('should save and retrieve a user', async () => {
const user = await UserService.create({ email: '[email protected]', name: 'Test' });
const found = await UserService.findById(user.id);
expect(found.email).toBe('[email protected]');
});
Integration test rules:
- Use a real test database — not a mock
- Clean up data before or after each test (use transactions that rollback, or seed scripts)
- Slower than unit tests — run on CI but not on every file save
Mocking Principles
Mocks replace external dependencies. Use them accurately.
// ❌ Mock that returns nothing useful
vi.mock('./mailer', () => ({ send: vi.fn() }));
// ✅ Mock that reflects real behavior
vi.mock('./mailer', () => ({
send: vi.fn().mockResolvedValue({ messageId: 'mock-id-123' }),
}));
What to mock:
- External HTTP calls (payment gateways, third-party APIs)
- Time (
Date.now(),new Date()) when time-dependent - File system in unit tests
- Email/SMS sending
What not to mock:
- Your own business logic
- The function being tested
- Simple pure utility functions
Test Coverage
Coverage measures which lines are executed during tests. 100% coverage does not mean the code is correct.
Useful coverage:
> 80%for business logic modules- Focus on statement + branch coverage (not just line coverage)
- Low coverage on infrastructure/config files is acceptable
Coverage anti-patterns:
- Tests written solely to increase coverage numbers
- Asserting that mocks were called instead of asserting real outcomes
- Tautology tests:
expect(result).toBe(result)
Scripts
| Script | Purpose | Run With |
|---|---|---|
scripts/test_runner.py | Runs test suite and reports results | python scripts/test_runner.py <project_path> |
Output Format
When this skill produces or reviews code, structure your output as follows:
━━━ Testing Patterns Report ━━━━━━━━━━━━━━━━━━━━━━━━
Skill: Testing Patterns
Language: [detected language / framework]
Scope: [N files · N functions]
─────────────────────────────────────────────────
✅ Passed: [checks that passed, or "All clean"]
⚠️ Warnings: [non-blocking issues, or "None"]
❌ Blocked: [blocking issues requiring fix, or "None"]
─────────────────────────────────────────────────
VBC status: PENDING → VERIFIED
Evidence: [test output / lint pass / compile success]
VBC (Verification-Before-Completion) is mandatory. Do not mark status as VERIFIED until concrete terminal evidence is provided.
🏛️ Tribunal Integration (Anti-Hallucination)
Slash command: /test
Active reviewers: logic · test-coverage-reviewer
❌ Forbidden AI Tropes in Testing
- Tautological Tests — writing
expect(x).toBe(x)or testing that a mock returns what it was mocked to return, ignoring actual behavior. - Missing the "Arrange" Step — relying on test state leftover from a previous
it()block. - Over-mocking — mocking the system under test or mocking out so much that the test is completely useless.
- Assertions in loops — writing loops with dynamic assertions instead of explicit, readable test cases.
- Ignoring Async Failures — forgetting to
awaitthe assertions or the function under test, leading to false positives.
✅ Pre-Flight Self-Audit
Review these questions before generating test code:
✅ Does each test explicitly follow the Arrange-Act-Assert (AAA) pattern?
✅ Are external dependencies (DBs, Networks) isolated efficiently using proper mocks?
✅ Is the test asserting verifying behavioral OUTCOMES rather than internal implementation details?
✅ Did I properly `await` asynchronous operations to prevent false positives?
✅ Are test names readable and descriptive of the tested behavior?