npx claudepluginhub yonatangross/orchestkit --plugin orkWant just this skill?
Add to a custom plugin, then install with one command.
Unit testing patterns for isolated business logic tests — AAA pattern, parametrized tests (test.each, @pytest.mark.parametrize), fixture scoping (function/module/session), mocking with MSW/VCR at network level, and test data management with factories (FactoryBoy, faker-js). Use when writing unit tests, setting up mocks, structuring test data, optimizing test speed, choosing fixture scope, or reducing test boilerplate. Covers Vitest, Jest, pytest.
This skill is limited to using the following tools:
checklists/msw-setup-checklist.mdchecklists/test-data-checklist.mdchecklists/vcr-checklist.mdexamples/handler-patterns.mdreferences/aaa-pattern.mdreferences/factory-patterns.mdreferences/msw-2x-api.mdreferences/stateful-testing.mdrules/_sections.mdrules/data-factories.mdrules/data-fixtures.mdrules/data-seeding-cleanup.mdrules/mocking-msw.mdrules/mocking-vcr.mdrules/unit-aaa-pattern.mdrules/unit-fixture-scoping.mdrules/unit-parametrized.mdscripts/create-msw-handler.mdscripts/create-test-case.mdscripts/create-test-fixture.mdUnit Testing Patterns
Focused patterns for writing isolated, fast, maintainable unit tests. Covers test structure (AAA), parametrization, fixture management, HTTP mocking (MSW/VCR), and test data generation with factories.
Each category has individual rule files in rules/ loaded on-demand, plus reference material, checklists, and scaffolding scripts.
Core Principles (ALWAYS apply)
- AAA structure: Every test MUST follow Arrange-Act-Assert. Use
// Arrange,// Act,// Assertcomments for clarity. - Parametrize, don't duplicate: Use
test.each(TypeScript) or@pytest.mark.parametrize(Python) when testing multiple inputs. Never copy-paste the same test body with different values. - Fixture scoping matters: Use
scope="function"(default) for mutable data. Usescope="module"orscope="session"ONLY for expensive read-only resources (DB engines, ML models). Mutable data with shared scope causes flaky tests. - Speed target: Each unit test should run under 100ms. If it's slower, you're likely hitting I/O — mock it.
- Mock at the network level: Use MSW (TypeScript) or VCR.py (Python) to intercept HTTP at the network layer. Never mock
fetch/axios/requestsdirectly.
Quick Reference
| Category | Rules | Impact | When to Use |
|---|---|---|---|
| Unit Test Structure | 3 | CRITICAL | Writing any unit test |
| HTTP Mocking | 2 | HIGH | Mocking API calls in frontend/backend tests |
| Test Data Management | 3 | MEDIUM | Setting up test data, factories, fixtures |
Total: 8 rules across 3 categories, 4 references, 3 checklists, 1 example set, 3 scripts
Unit Test Structure
Core patterns for structuring isolated unit tests with clear phases and efficient execution.
| Rule | File | Key Pattern |
|---|---|---|
| AAA Pattern | rules/unit-aaa-pattern.md | Arrange-Act-Assert with isolation |
| Fixture Scoping | rules/unit-fixture-scoping.md | function/module/session scope selection |
| Parametrized Tests | rules/unit-parametrized.md | test.each / @pytest.mark.parametrize |
Reference: references/aaa-pattern.md — detailed AAA implementation with checklist
HTTP Mocking
Network-level request interception for deterministic tests without hitting real APIs.
| Rule | File | Key Pattern |
|---|---|---|
| MSW 2.x | rules/mocking-msw.md | Network-level mocking for frontend (TypeScript) |
| VCR.py | rules/mocking-vcr.md | Record/replay HTTP cassettes (Python) |
References:
references/msw-2x-api.md— full MSW 2.x API (handlers, GraphQL, WebSocket, passthrough)references/stateful-testing.md— Hypothesis RuleBasedStateMachine for stateful tests
Checklists:
checklists/msw-setup-checklist.md— MSW installation, handler setup, test writingchecklists/vcr-checklist.md— VCR configuration, sensitive data filtering, CI setup
Examples: examples/handler-patterns.md — CRUD, error simulation, auth flow, file upload handlers
Test Data Management
Factories, fixtures, and seeding patterns for isolated, realistic test data.
| Rule | File | Key Pattern |
|---|---|---|
| Data Factories | rules/data-factories.md | FactoryBoy / @faker-js builders |
| Data Fixtures | rules/data-fixtures.md | JSON fixtures with composition |
| Seeding & Cleanup | rules/data-seeding-cleanup.md | Automated DB seeding and teardown |
Reference: references/factory-patterns.md — advanced factory patterns (Sequence, SubFactory, Traits)
Checklist: checklists/test-data-checklist.md — data generation, cleanup, isolation verification
Quick Start
TypeScript (Vitest + MSW)
import { describe, test, expect, beforeAll, afterEach, afterAll } from 'vitest';
import { http, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { calculateDiscount } from './pricing';
// 1. Pure unit test with AAA pattern
describe('calculateDiscount', () => {
test.each([
[100, 0],
[150, 15],
[200, 20],
])('for order $%i returns $%i discount', (total, expected) => {
// Arrange
const order = { total };
// Act
const discount = calculateDiscount(order);
// Assert
expect(discount).toBe(expected);
});
});
// 2. MSW mocked API test
const server = setupServer(
http.get('/api/users/:id', ({ params }) => {
return HttpResponse.json({ id: params.id, name: 'Test User' });
})
);
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('fetches user from API', async () => {
// Arrange — MSW handler set up above
// Act
const response = await fetch('/api/users/123');
const data = await response.json();
// Assert
expect(data.name).toBe('Test User');
});
Python (pytest + FactoryBoy)
import pytest
from factory import Factory, Faker, SubFactory
class UserFactory(Factory):
class Meta:
model = dict
email = Faker('email')
name = Faker('name')
class TestUserService:
@pytest.mark.parametrize("role,can_edit", [
("admin", True),
("viewer", False),
])
def test_edit_permission(self, role, can_edit):
# Arrange
user = UserFactory(role=role)
# Act
result = user_can_edit(user)
# Assert
assert result == can_edit
Vitest 4.1 Features
aroundEach / aroundAll (preferred for DB transactions)
Wraps each test in setup/teardown — cleaner than separate beforeEach/afterEach for transactions:
test.aroundEach(async (runTest, { db }) => {
await db.transaction(runTest) // auto-rollback on test end
})
test('insert user', async ({ db }) => {
await db.insert({ name: 'Alice' })
// transaction auto-rolls back — no cleanup needed
})
aroundAll wraps entire suites the same way.
mockThrow / mockThrowOnce
Replaces the verbose mockImplementation(() => { throw err }) pattern:
const fn = vi.fn()
fn.mockThrow(new Error('connection lost')) // always throws
fn.mockThrowOnce(new Error('timeout')) // throws once, then normal
vi.defineHelper (clean stack traces)
Custom assertion helpers that point errors to the call site, not the helper internals:
const assertPair = vi.defineHelper((a, b) => {
expect(a).toEqual(b) // error points to where assertPair() was CALLED
})
Test Tags
Filter tests by tags in CLI — useful for CI fast paths:
// vitest.config.ts
test: {
tags: {
unit: { timeout: 5000 },
flaky: { retry: 3 },
}
}
vitest --tags-filter="unit and !flaky"
vitest --tags-filter="(unit or integration) and !slow"
Agent Reporter
Minimal output (failures only) — use in AI agent / CI contexts:
AI_AGENT=copilot vitest # auto-detect agent mode
Key Decisions
| Decision | Recommendation |
|---|---|
| Test framework (TS) | Vitest 4.1+ (modern, fast, aroundEach, test tags) or Jest (mature ecosystem) |
| Test framework (Python) | pytest with plugins (parametrize, asyncio, cov) |
| HTTP mocking (TS) | MSW 2.x at network level, never mock fetch/axios directly |
| HTTP mocking (Python) | VCR.py with cassettes, filter sensitive data |
| Test data | Factories (FactoryBoy/faker-js) over hardcoded fixtures |
| Fixture scope | scope="function" for mutable (default). module/session ONLY for expensive immutable resources |
| Execution time | Under 100ms per unit test — if slower, mock external calls |
| Coverage target | 90%+ business logic, 100% critical paths |
Common Mistakes
- Testing implementation details instead of public behavior (brittle tests)
- Mocking fetch/axios directly instead of using MSW at network level (incomplete coverage)
- Shared mutable state between tests via module-scoped fixtures (flaky tests)
- Hard-coded test data with duplicate IDs (test conflicts in parallel runs)
- No cleanup after database seeding (state leaks between tests)
- Over-mocking — testing your mocks instead of your code (false confidence)
- Verbose throw mocking —
mockImplementation(() => { throw err })instead ofmockThrow(err)(Vitest 4.1+)
Scripts
| Script | File | Purpose |
|---|---|---|
| Create Test Case | scripts/create-test-case.md | Scaffold test file with auto-detected framework |
| Create Test Fixture | scripts/create-test-fixture.md | Scaffold pytest fixture with context detection |
| Create MSW Handler | scripts/create-msw-handler.md | Scaffold MSW handler for an API endpoint |