install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/test-unit-patterns" ~/.claude/skills/intense-visions-harness-engineering-test-unit-patterns-3c7a7f && rm -rf "$T"
manifest:
agents/skills/codex/test-unit-patterns/SKILL.mdsource content
Test Unit Patterns
Write focused, isolated unit tests using AAA pattern with describe/it/expect
When to Use
- Testing individual functions, classes, or modules in isolation
- Verifying pure logic, calculations, or data transformations
- Building a fast test suite that runs in milliseconds
- Following TDD red-green-refactor workflow
Instructions
- Structure tests with AAA (Arrange, Act, Assert):
import { describe, it, expect } from 'vitest'; import { calculateDiscount } from './pricing'; describe('calculateDiscount', () => { it('applies 10% discount for orders over $100', () => { // Arrange const orderTotal = 150; const discountRate = 0.1; // Act const result = calculateDiscount(orderTotal, discountRate); // Assert expect(result).toBe(15); }); });
- One assertion per test (or one logical assertion group):
it('returns the created user with all fields', () => { const user = createUser({ name: 'Alice', email: 'alice@test.com' }); expect(user).toMatchObject({ name: 'Alice', email: 'alice@test.com', }); expect(user.id).toBeDefined(); expect(user.createdAt).toBeInstanceOf(Date); });
- Use
blocks to group related tests:describe
describe('UserService', () => { describe('create', () => { it('creates a user with valid input', () => { /* ... */ }); it('throws on duplicate email', () => { /* ... */ }); it('normalizes email to lowercase', () => { /* ... */ }); }); describe('findById', () => { it('returns the user when found', () => { /* ... */ }); it('returns null when not found', () => { /* ... */ }); }); });
- Test edge cases explicitly:
describe('parseAge', () => { it('parses valid integer strings', () => { expect(parseAge('25')).toBe(25); }); it('returns null for empty string', () => { expect(parseAge('')).toBeNull(); }); it('returns null for non-numeric input', () => { expect(parseAge('abc')).toBeNull(); }); it('returns null for negative numbers', () => { expect(parseAge('-5')).toBeNull(); }); it('returns null for floating point', () => { expect(parseAge('25.5')).toBeNull(); }); it('handles zero', () => { expect(parseAge('0')).toBe(0); }); });
- Test error cases with
:toThrow
it('throws on invalid email', () => { expect(() => validateEmail('not-an-email')).toThrow('Invalid email'); }); it('throws specific error type', () => { expect(() => validateEmail('')).toThrow(ValidationError); });
- Use
/beforeEach
for shared setup:afterEach
describe('CartService', () => { let cart: CartService; beforeEach(() => { cart = new CartService(); }); it('starts empty', () => { expect(cart.items).toHaveLength(0); }); it('adds items', () => { cart.add({ id: '1', qty: 2 }); expect(cart.items).toHaveLength(1); }); });
- Test async functions:
it('resolves with user data', async () => { const user = await getUser('123'); expect(user.name).toBe('Alice'); }); it('rejects with not found error', async () => { await expect(getUser('nonexistent')).rejects.toThrow('User not found'); });
- Name tests as behavior specifications — describe what the code does, not how it does it:
// Good: describes behavior it('applies free shipping for orders over $50', () => { /* ... */ }); // Bad: describes implementation it('calls calculateShipping with zero', () => { /* ... */ });
Details
Unit tests verify individual units of code (functions, classes, modules) in isolation from external dependencies. They should be fast (< 10ms each), deterministic, and independent of execution order.
What makes a good unit test:
- Tests one behavior per test case
- Does not depend on other tests or external state
- Fails for exactly one reason
- Is readable as a specification of the code's behavior
Test file organization: Place test files next to the source file (
user.ts → user.test.ts) or in a parallel __tests__ directory. Co-location makes it easy to find tests and keeps imports short.
matcher selection:expect
— strict equality (toBe
). Use for primitives===
— deep equality. Use for objects and arraystoEqual
— partial deep equality. Object under test may have extra propertiestoMatchObject
— array includes element or string includes substringtoContain
— array or string lengthtoHaveLength
/toBeGreaterThan
— numeric comparisonstoBeLessThan
— function throws. Wrap the call in an arrow functiontoThrow
Trade-offs:
- High unit test coverage gives fast feedback — but can over-test implementation details
- Isolated tests are fast — but may miss integration issues
- AAA structure is clear — but can feel verbose for simple one-liner tests
is flexible — but can pass when extra unexpected properties existtoMatchObject
Source
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.