.build/codex/skills/testing-patterns/SKILL.md
Write effective tests across the stack—unit, integration, E2E, and visual regression. Covers testing philosophy, framework selection, mocking strategies, and CI integration. Triggers on testing, test coverage, TDD, or quality assurance requests.
npx skillsauth add organvm-iv-taxis/a-i--skills testing-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build confidence through strategic testing.
╱╲
╱ ╲ E2E (few)
╱────╲
╱ ╲ Integration (more)
╱────────╲
╱ ╲ Unit (many, fast)
╱────────────╲
╱ Static ╲ TypeScript, ESLint
╱────────────────╲
| Test Type | What | Why | |-----------|------|-----| | Static | Types, lint rules | Catch errors at write-time | | Unit | Pure functions, utils | Fast, precise feedback | | Integration | Component + dependencies | Test contracts | | E2E | User flows | Confidence in real usage |
describe('calculateTotal', () => {
it('should apply discount to subtotal', () => {
// Arrange
const items = [{ price: 100 }, { price: 50 }];
const discount = 0.1;
// Act
const result = calculateTotal(items, discount);
// Assert
expect(result).toBe(135);
});
});
// Pattern: should [expected behavior] when [condition]
it('should return empty array when input is null')
it('should throw error when user is not authenticated')
it('should apply discount when coupon is valid')
// utils/format.ts
export function formatCurrency(cents: number): string {
return `$${(cents / 100).toFixed(2)}`;
}
// utils/format.test.ts
describe('formatCurrency', () => {
it('should format cents to dollar string', () => {
expect(formatCurrency(1000)).toBe('$10.00');
expect(formatCurrency(1)).toBe('$0.01');
expect(formatCurrency(0)).toBe('$0.00');
});
it('should handle negative values', () => {
expect(formatCurrency(-500)).toBe('$-5.00');
});
});
"The more your tests resemble the way your software is used, the more confidence they can give you."
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';
describe('Counter', () => {
it('should increment count when button clicked', async () => {
render(<Counter initialCount={0} />);
const button = screen.getByRole('button', { name: /increment/i });
await fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
});
Use queries in this order (most to least preferred):
getByRole - Accessible to everyonegetByLabelText - Form fieldsgetByPlaceholderText - If no labelgetByText - Non-interactive contentgetByTestId - Last resortimport { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
it('should load data after button click', async () => {
const user = userEvent.setup();
render(<DataLoader />);
await user.click(screen.getByRole('button', { name: /load/i }));
// Wait for async content
await waitFor(() => {
expect(screen.getByText('Data loaded')).toBeInTheDocument();
});
});
// Mock a module
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ name: 'Test User' })),
}));
// Mock a hook
vi.mock('./useAuth', () => ({
useAuth: () => ({ user: { id: '1' }, isLoading: false }),
}));
// Mock fetch
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: 'test' }),
})
);
import { createMocks } from 'node-mocks-http';
import handler from './api/posts';
describe('/api/posts', () => {
it('should return posts list', async () => {
const { req, res } = createMocks({
method: 'GET',
});
await handler(req, res);
expect(res._getStatusCode()).toBe(200);
expect(JSON.parse(res._getData())).toHaveLength(3);
});
});
import { db } from '@/lib/db';
describe('User service', () => {
beforeEach(async () => {
await db.user.deleteMany(); // Clean state
});
afterAll(async () => {
await db.$disconnect();
});
it('should create user with valid data', async () => {
const user = await createUser({
email: '[email protected]',
name: 'Test User'
});
expect(user.id).toBeDefined();
expect(user.email).toBe('[email protected]');
});
});
// e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Authentication', () => {
test('should allow user to sign in', async ({ page }) => {
await page.goto('/login');
await page.fill('[name="email"]', '[email protected]');
await page.fill('[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Welcome');
});
});
// e2e/pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) { <!-- allow-secret -->
await this.page.fill('[name="email"]', email);
await this.page.fill('[name="password"]', password);
await this.page.click('button[type="submit"]');
}
}
// e2e/auth.spec.ts
test('should login successfully', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('[email protected]', 'password');
await expect(page).toHaveURL('/dashboard');
});
test('homepage should match snapshot', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png');
});
| Mock | When | |------|------| | External APIs | Always in unit/integration | | Database | Sometimes (test containers vs mocks) | | Time/Date | When testing time-dependent logic | | Randomness | When testing deterministic output | | Network | Always in unit tests |
// mocks/handlers.ts
import { rest } from 'msw';
export const handlers = [
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.json([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
])
);
}),
rest.post('/api/users', async (req, res, ctx) => {
const body = await req.json();
return res(ctx.status(201), ctx.json({ id: 3, ...body }));
}),
];
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['./test/setup.ts'],
coverage: {
reporter: ['text', 'html'],
exclude: ['node_modules/', 'test/'],
},
},
});
// test/setup.ts
import '@testing-library/jest-dom';
import { server } from './mocks/server';
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
| Type | Target | Notes | |------|--------|-------| | Statements | 70-80% | Don't chase 100% | | Branches | 70-80% | Test important paths | | Functions | 80%+ | All public APIs | | Lines | 70-80% | Balance with velocity |
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run test:coverage
- uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
references/vitest-patterns.md - Vitest specific patternsreferences/playwright-patterns.md - E2E testing patternsreferences/mock-examples.md - Mocking recipesdevelopment
Optimize resumes and CVs for impact, ATS compatibility, and audience targeting. Supports multiple formats (chronological, functional, hybrid), accomplishment framing (STAR/XYZ), and tailoring for specific roles. Triggers on resume review, CV update, job application prep, or career document requests.
testing
Transfer context between AI agent sessions with structured handoff protocols, state serialization, and decision log preservation. Covers multi-agent coordination, context compression, and continuity patterns. Triggers on agent handoff, session transfer, or multi-agent continuity requests.
tools
Craft compelling fiction and creative nonfiction with attention to structure, voice, prose style, and revision. Supports short stories, novel chapters, essays, and hybrid forms. Triggers on creative writing, fiction writing, story craft, prose style, or literary technique requests.
devops
Transform AI conversations and chat transcripts into publishable content including blog posts, documentation, tutorials, and knowledge base entries. Covers extraction, restructuring, and editorial refinement. Triggers on conversation-to-content, transcript processing, or chat-to-doc requests.