Use when writing tests, setting up test frameworks, implementing mocking strategies, or establishing testing best practices (unit, integration, E2E) across any technology stack.
Language-agnostic testing patterns and strategies applicable across all technology stacks.
βββββββββ
β E2E β Few, slow, expensive
ββ΄ββββββββ΄β
βββββββββββββ
βIntegrationβ Some, medium speed
ββ΄ββββββββββββ΄β
βββββββββββββββββ
β Unit β Many, fast, cheap
ββ΄ββββββββββββββββ΄β
// Arrange - Set up test data and conditions
const user = createTestUser({ email: 'test@example.com' });
const service = new UserService(mockDb);
// Act - Execute the code under test
const result = await service.createUser(user);
// Assert - Verify the expected outcome
expect(result.id).toBeDefined();
expect(result.email).toBe('test@example.com');
component.test.ts # Unit tests
component.spec.ts # Alternative convention
component.integration.ts # Integration tests
component.e2e.ts # End-to-end tests
Use descriptive names that explain the scenario:
// Pattern: should [expected behavior] when [condition]
describe('UserService', () => {
describe('createUser', () => {
it('should create user when valid data provided', () => {});
it('should throw ValidationError when email is invalid', () => {});
it('should throw DuplicateError when email already exists', () => {});
});
});
describe('User Registration', () => {
describe('given a valid email and password', () => {
describe('when the user submits the form', () => {
it('then creates a new account', () => {});
it('then sends a welcome email', () => {});
});
});
});
| Type | Purpose | Example |
|---|---|---|
| Stub | Returns predetermined values | stub.returns(42) |
| Mock | Verifies interactions | expect(mock).toHaveBeenCalled() |
| Spy | Wraps real implementation | spy(realFunction) |
| Fake | Working implementation (simplified) | In-memory database |
| Dummy | Placeholder (not used) | Required parameter |
DO Mock:
DON'T Mock:
// Mock external API
const mockApi = {
getUser: jest.fn().mockResolvedValue({ id: '1', name: 'Test' })
};
// Inject mock
const service = new UserService(mockApi);
const result = await service.getUser('1');
// Verify interaction
expect(mockApi.getUser).toHaveBeenCalledWith('1');
expect(result.name).toBe('Test');
Create reusable factory functions for test data:
// factories/user.ts
export function createTestUser(overrides = {}) {
return {
id: randomUUID(),
email: `test-${Date.now()}@example.com`,
name: 'Test User',
createdAt: new Date(),
...overrides
};
}
// In tests
const user = createTestUser({ name: 'Custom Name' });
Static test data for consistent testing:
// fixtures/users.ts
export const validUser = {
email: 'valid@example.com',
password: 'SecurePass123!'
};
export const invalidEmails = [
'no-at-sign',
'@no-local.com',
'no-domain@',
'spaces in@email.com'
];
// BAD - vague assertion
expect(result).toBeTruthy();
// GOOD - specific assertion
expect(result.success).toBe(true);
expect(result.data.id).toBe('expected-id');
// BAD - multiple unrelated assertions
it('should process order', () => {
expect(order.id).toBeDefined();
expect(order.total).toBe(100);
expect(emailService.send).toHaveBeenCalled();
expect(inventory.reduce).toHaveBeenCalled();
});
// GOOD - focused tests
it('should assign an order ID', () => {
expect(order.id).toBeDefined();
});
it('should calculate correct total', () => {
expect(order.total).toBe(100);
});
it('should send confirmation email', () => {
expect(emailService.send).toHaveBeenCalled();
});
Create domain-specific matchers for readability:
expect.extend({
toBeValidEmail(received) {
const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(received);
return {
pass,
message: () => `expected ${received} to be a valid email`
};
}
});
// Usage
expect('user@example.com').toBeValidEmail();
| Type | Target | Notes |
|---|---|---|
| Line Coverage | 80% | Minimum acceptable |
| Branch Coverage | 75% | Test all conditionals |
| Function Coverage | 90% | All public APIs |
| Critical Paths | 100% | Auth, payments, data integrity |
High coverage β good tests
// 100% coverage but useless test
it('should run without error', () => {
processOrder(order); // No assertions!
});
Focus on:
| Test Type | Target Time |
|---|---|
| Unit test | < 10ms |
| Integration test | < 1s |
| E2E test | < 30s |
| Full suite | < 5min |
# Run tests in parallel
vitest --pool=threads
jest --maxWorkers=4
pytest -n auto
go test -parallel 4
Tests should not depend on each other:
// BAD - shared state
let counter = 0;
it('test 1', () => { counter++; });
it('test 2', () => { expect(counter).toBe(1); }); // Fragile!
// GOOD - isolated state
beforeEach(() => { counter = 0; });
it('test 1', () => { counter++; expect(counter).toBe(1); });
it('test 2', () => { counter++; expect(counter).toBe(1); });
Testing strategies applicable to all technology stacks