testing-strategies

Testing Strategies Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "testing-strategies" with this command: npx skills add claudeforge/orchestrator/claudeforge-orchestrator-testing-strategies

Testing Strategies Skill

Comprehensive testing strategies and patterns for ensuring code quality.

Testing Pyramid

          /\
         /E2E\           5%  - Critical user journeys
        /------\
       / Integ  \        15% - Service boundaries
      /----------\
     /    Unit    \      80% - Component logic
    /--------------\

Unit Testing Patterns

Arrange-Act-Assert (AAA)

describe('calculateTotal', () => { it('should apply discount correctly', () => { // Arrange const items = [{ price: 100, quantity: 1 }]; const discount = 0.1;

// Act
const result = calculateTotal(items, discount);

// Assert
expect(result).toBe(90);

}); });

Test Isolation

describe('UserService', () => { let service: UserService; let mockRepo: MockUserRepository;

beforeEach(() => { // Fresh instances for each test mockRepo = new MockUserRepository(); service = new UserService(mockRepo); });

afterEach(() => { vi.clearAllMocks(); }); });

Parameterized Tests

describe('validateEmail', () => { it.each([ ['test@example.com', true], ['user.name@domain.co.uk', true], ['invalid', false], ['@nodomain.com', false], ['no@tld', false], ])('validates %s as %s', (email, expected) => { expect(validateEmail(email)).toBe(expected); }); });

Testing Edge Cases

describe('divide', () => { it('should handle positive numbers', () => { expect(divide(10, 2)).toBe(5); });

it('should handle negative numbers', () => { expect(divide(-10, 2)).toBe(-5); });

it('should handle zero dividend', () => { expect(divide(0, 5)).toBe(0); });

it('should throw on division by zero', () => { expect(() => divide(10, 0)).toThrow('Division by zero'); });

it('should handle floating point', () => { expect(divide(1, 3)).toBeCloseTo(0.333, 2); }); });

Component Testing

React Testing Library

import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event';

describe('LoginForm', () => { const mockOnSubmit = vi.fn();

beforeEach(() => { mockOnSubmit.mockClear(); });

it('submits with valid data', async () => { const user = userEvent.setup(); render(<LoginForm onSubmit={mockOnSubmit} />);

await user.type(screen.getByLabelText(/email/i), 'test@example.com');
await user.type(screen.getByLabelText(/password/i), 'password123');
await user.click(screen.getByRole('button', { name: /submit/i }));

await waitFor(() => {
  expect(mockOnSubmit).toHaveBeenCalledWith({
    email: 'test@example.com',
    password: 'password123',
  });
});

});

it('shows validation errors', async () => { const user = userEvent.setup(); render(<LoginForm onSubmit={mockOnSubmit} />);

await user.click(screen.getByRole('button', { name: /submit/i }));

expect(await screen.findByText(/email is required/i)).toBeInTheDocument();
expect(mockOnSubmit).not.toHaveBeenCalled();

}); });

Testing Hooks

import { renderHook, act, waitFor } from '@testing-library/react';

describe('useCounter', () => { it('increments counter', () => { const { result } = renderHook(() => useCounter(0));

act(() => {
  result.current.increment();
});

expect(result.current.count).toBe(1);

}); });

describe('useAsync', () => { it('handles async operation', async () => { const mockFetch = vi.fn().mockResolvedValue({ data: 'test' });

const { result } = renderHook(() => useAsync(mockFetch));

expect(result.current.isLoading).toBe(true);

await waitFor(() => {
  expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toEqual({ data: 'test' });

}); });

Integration Testing

API Testing with Hono

import { testClient } from 'hono/testing'; import app from '../src/index';

describe('Users API', () => { const client = testClient(app);

beforeEach(async () => { await db.delete(users); });

it('creates and retrieves user', async () => { // Create const createRes = await client.api.v1.users.$post({ json: { name: 'Test', email: 'test@example.com', password: 'pass123' }, }); expect(createRes.status).toBe(201); const created = await createRes.json();

// Retrieve
const getRes = await client.api.v1.users[':id'].$get({
  param: { id: created.data.id },
});
expect(getRes.status).toBe(200);
const retrieved = await getRes.json();

expect(retrieved.data.email).toBe('test@example.com');

}); });

Database Testing

describe('UserRepository', () => { const repo = new UserRepository();

beforeEach(async () => { await db.delete(users); });

it('creates and finds user', async () => { const created = await repo.create({ name: 'Test User', email: 'test@example.com', });

const found = await repo.findById(created.id);

expect(found).toEqual(created);

});

it('returns null for non-existent user', async () => { const found = await repo.findById('non-existent-id'); expect(found).toBeNull(); }); });

E2E Testing with Playwright

import { test, expect } from '@playwright/test';

test.describe('User Flow', () => { test('complete registration and login flow', async ({ page }) => { // Register await page.goto('/register'); await page.fill('[name="email"]', 'new@example.com'); await page.fill('[name="password"]', 'Password123!'); await page.click('button[type="submit"]');

// Verify redirect to login
await expect(page).toHaveURL('/login');

// Login
await page.fill('[name="email"]', 'new@example.com');
await page.fill('[name="password"]', 'Password123!');
await page.click('button[type="submit"]');

// Verify dashboard access
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Dashboard');

}); });

Mocking Strategies

MSW (Mock Service Worker)

import { http, HttpResponse } from 'msw'; import { setupServer } from 'msw/node';

const handlers = [ http.get('/api/users', () => { return HttpResponse.json({ success: true, data: [{ id: '1', name: 'Test User' }], }); }),

http.post('/api/users', async ({ request }) => { const body = await request.json(); return HttpResponse.json( { success: true, data: { id: '2', ...body } }, { status: 201 } ); }), ];

export const server = setupServer(...handlers);

// Setup in test file beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close());

Vitest Mocks

// Mock module vi.mock('./api', () => ({ fetchUsers: vi.fn().mockResolvedValue([{ id: '1', name: 'Test' }]), }));

// Mock implementation const mockFetch = vi.fn(); mockFetch .mockResolvedValueOnce({ data: 'first' }) .mockResolvedValueOnce({ data: 'second' });

// Spy on method const spy = vi.spyOn(console, 'log'); expect(spy).toHaveBeenCalledWith('message');

Coverage Goals

Metric Minimum Target

Statements 70% 85%

Branches 65% 80%

Functions 70% 85%

Lines 70% 85%

Test Organization

tests/ ├── unit/ # Unit tests │ ├── utils/ │ └── services/ ├── integration/ # Integration tests │ ├── api/ │ └── db/ ├── e2e/ # E2E tests │ ├── auth.spec.ts │ └── dashboard.spec.ts ├── fixtures/ # Test data ├── mocks/ # Mock implementations └── setup.ts # Global setup

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

project-planning

No summary provided by upstream source.

Repository SourceNeeds Review
General

backend-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

frontend-patterns

No summary provided by upstream source.

Repository SourceNeeds Review