testing-strategies
The testing-strategies Claude Code skill provides language-agnostic testing patterns including the testing pyramid framework, unit/integration/E2E test ratios, the AAA test structure pattern, naming conventions for test files and cases, and mocking strategies. Use this skill when designing test architecture, establishing testing best practices, implementing mocking approaches, or structuring test suites across any technology stack.
git clone --depth 1 https://github.com/MadAppGang/claude-code /tmp/testing-strategies && cp -r /tmp/testing-strategies/plugins/dev/skills/core/testing-strategies ~/.claude/skills/testing-strategiesSKILL.md
# Universal Testing Strategies
## Overview
Language-agnostic testing patterns and strategies applicable across all technology stacks.
## Testing Pyramid
```
┌───────┐
│ E2E │ Few, slow, expensive
─┴───────┴─
┌───────────┐
│Integration│ Some, medium speed
─┴───────────┴─
┌───────────────┐
│ Unit │ Many, fast, cheap
─┴───────────────┴─
```
### Unit Tests (70-80%)
- Test individual functions/methods in isolation
- Mock external dependencies
- Fast execution (< 10ms per test)
- High volume, run on every save
### Integration Tests (15-20%)
- Test component interactions
- Real database/API calls (often with test containers)
- Medium speed (100ms - 1s per test)
- Run on commit/PR
### End-to-End Tests (5-10%)
- Test complete user flows
- Real browser/environment
- Slow (seconds to minutes)
- Run before deployment
## Test Structure: AAA Pattern
```
// 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');
```
## Naming Conventions
### Test File Names
```
component.test.ts # Unit tests
component.spec.ts # Alternative convention
component.integration.ts # Integration tests
component.e2e.ts # End-to-end tests
```
### Test Case Names
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', () => {});
});
});
```
### BDD Style (Given-When-Then)
```
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', () => {});
});
});
});
```
## Mocking Strategies
### Test Doubles Overview
| 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 |
### When to Mock
**DO Mock:**
- External services (APIs, databases)
- Time-dependent functions
- Random number generators
- File system operations
- Network requests
**DON'T Mock:**
- The code under test
- Simple value objects
- Pure functions with no side effects
### Mock Example
```typescript
// 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');
```
## Test Data Management
### Test Factories
Create reusable factory functions for test data:
```typescript
// 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' });
```
### Fixtures
Static test data for consistent testing:
```typescript
// 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'
];
```
## Assertion Best Practices
### Be Specific
```typescript
// BAD - vague assertion
expect(result).toBeTruthy();
// GOOD - specific assertion
expect(result.success).toBe(true);
expect(result.data.id).toBe('expected-id');
```
### One Logical Assertion Per Test
```typescript
// 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();
});
```
### Custom Matchers
Create domain-specific matchers for readability:
```typescript
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();
```
## Edge Cases to Test
### Input Validation
- Empty/null/undefined inputs
- Boundary values (0, -1, MAX_INT)
- Invalid types
- Malformed data
### State Transitions
- Initial state
- After single operation
- After multiple operations
- After error and recovery
### Async Operations
- Successful completion
- Timeout
- Network errors
- Concurrent operations
- Race conditions
### Error Handling
- Expected errors (validation)
- Unexpected errors (system)
- Error recovery
- Error propagation
## Code Coverage Guidelines
### Coverage Targets
| 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 |
### Coverag|
|
|
Common agent patterns and templates for Claude Code. Use when implementing agents to follow proven patterns for Tasks integration, quality checks, and external model invocation via claudish CLI.
YAML frontmatter schemas for Claude Code agents and commands. Use when creating or validating agent/command files.
XML tag structure patterns for Claude Code agents and commands. Use when designing or implementing agents to ensure proper XML structure following Anthropic best practices.
YAML format for Claude Code agent definitions as alternative to markdown. Use when creating agents with YAML, converting markdown agents to YAML, or validating YAML agent schemas. Trigger keywords - "YAML agent", "agent YAML", "YAML format", "agent schema", "YAML definition", "convert to YAML".
Linear API patterns and examples for autopilot. Includes authentication, webhooks, issue CRUD, state transitions, file attachments, and comment handling.