designing-tests

SKILL.md

Designing Tests

Test Implementation Workflow

Copy this checklist and track progress:

Test Implementation Progress:
- [ ] Step 1: Identify what to test
- [ ] Step 2: Select appropriate test type
- [ ] Step 3: Write tests following templates
- [ ] Step 4: Run tests and verify passing
- [ ] Step 5: Check coverage meets targets
- [ ] Step 6: Fix any failing tests

Testing Pyramid

Apply the testing pyramid for balanced coverage:

        /\
       /  \     E2E Tests (10%)
      /----\    - Critical user journeys
     /      \   - Slow but comprehensive
    /--------\  Integration Tests (20%)
   /          \ - Component interactions
  /------------\ - API contracts
 /              \ Unit Tests (70%)
/________________\ - Fast, isolated
                   - Business logic focus

Framework Selection

JavaScript/TypeScript

Type Recommended Alternative
Unit Vitest Jest
Integration Vitest + MSW Jest + SuperTest
E2E Playwright Cypress
Component Testing Library Enzyme

Python

Type Recommended Alternative
Unit pytest unittest
Integration pytest + httpx pytest + requests
E2E Playwright Selenium
API pytest + FastAPI TestClient -

Go

Type Recommended
Unit testing + testify
Integration testing + httptest
E2E testing + chromedp

Test Structure Templates

Unit Test

describe('[Unit] ComponentName', () => {
  describe('methodName', () => {
    it('should [expected behavior] when [condition]', () => {
      // Arrange
      const input = createTestInput();

      // Act
      const result = methodName(input);

      // Assert
      expect(result).toEqual(expectedOutput);
    });

    it('should throw error when [invalid condition]', () => {
      expect(() => methodName(invalidInput)).toThrow(ExpectedError);
    });
  });
});

Integration Test

describe('[Integration] API /users', () => {
  beforeAll(async () => {
    await setupTestDatabase();
  });

  afterAll(async () => {
    await teardownTestDatabase();
  });

  it('should create user and return 201', async () => {
    const response = await request(app)
      .post('/users')
      .send({ name: 'Test', email: 'test@example.com' });

    expect(response.status).toBe(201);
    expect(response.body.id).toBeDefined();
  });
});

E2E Test

describe('[E2E] User Registration Flow', () => {
  it('should complete registration successfully', async ({ page }) => {
    await page.goto('/register');

    await page.fill('[data-testid="email"]', 'new@example.com');
    await page.fill('[data-testid="password"]', 'SecurePass123!');
    await page.click('[data-testid="submit"]');

    await expect(page.locator('.welcome-message')).toBeVisible();
    await expect(page).toHaveURL('/dashboard');
  });
});

Coverage Strategy

What to Cover

  • ✅ Business logic (100%)
  • ✅ Edge cases and error handling (90%+)
  • ✅ API contracts (100%)
  • ✅ Critical user paths (E2E)
  • ⚠️ UI components (snapshot + interaction)
  • ❌ Third-party library internals
  • ❌ Simple getters/setters

Coverage Thresholds

{
  "coverageThreshold": {
    "global": {
      "branches": 80,
      "functions": 80,
      "lines": 80,
      "statements": 80
    },
    "src/core/": {
      "branches": 95,
      "functions": 95
    }
  }
}

Test Data Management

Factories/Builders

// factories/user.js
export const userFactory = (overrides = {}) => ({
  id: faker.string.uuid(),
  name: faker.person.fullName(),
  email: faker.internet.email(),
  createdAt: new Date(),
  ...overrides,
});

// Usage
const admin = userFactory({ role: 'admin' });

Fixtures

// fixtures/users.json
{
  "validUser": { "name": "Test", "email": "test@example.com" },
  "invalidUser": { "name": "", "email": "invalid" }
}

Mocking Strategy

When to Mock

  • ✅ External APIs and services
  • ✅ Database in unit tests
  • ✅ Time/Date for determinism
  • ✅ Random values
  • ❌ Internal modules (usually)
  • ❌ The code under test

Mock Examples

// API mocking with MSW
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'John' },
    ]);
  }),
];

// Time mocking
vi.useFakeTimers();
vi.setSystemTime(new Date('2024-01-01'));

Test Validation Loop

After writing tests, run this validation:

Test Validation:
- [ ] All tests pass: `npm test`
- [ ] Coverage meets thresholds: `npm test -- --coverage`
- [ ] No flaky tests (run multiple times)
- [ ] Tests are independent (order doesn't matter)
- [ ] Test names clearly describe behavior

If any tests fail, fix them before proceeding. If coverage is below target, add more tests for uncovered code paths.

# Run tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npm test -- path/to/test.spec.ts

# Run in watch mode during development
npm test -- --watch
Weekly Installs
3
Installed on
trae2
claude-code2
kilo1
windsurf1
amp1
clawdbot1