Testing Suite
Complete testing infrastructure for modern TypeScript/JavaScript applications.
Decision Tree
User request → What type of testing?
│
├─ Unit Tests
│ ├─ Runner?
│ │ ├─ Vitest → Fast, Vite-native, ESM
│ │ ├─ Jest → Mature, wide ecosystem
│ │ └─ Bun test → Ultra-fast, built-in
│ │
│ └─ What to test?
│ ├─ Functions → Pure logic, utilities
│ ├─ Components → React Testing Library
│ ├─ Hooks → @testing-library/react-hooks
│ └─ API handlers → MSW for mocking
│
├─ Integration Tests
│ ├─ Database → Test containers, in-memory
│ ├─ API → Supertest, actual endpoints
│ └─ Services → Real dependencies
│
├─ E2E Tests
│ ├─ Playwright → Cross-browser, fast
│ ├─ Cypress → Great DX, dashboard
│ └─ What to test?
│ ├─ User flows → Critical paths
│ ├─ Visual regression → Screenshots
│ └─ Accessibility → axe-core
│
└─ Test Strategy
├─ TDD → Red-green-refactor
├─ Coverage → Istanbul/c8
└─ CI → GitHub Actions integration
Quick Start
Vitest Setup
# Install
pnpm add -D vitest @vitest/coverage-v8 @vitest/ui
# With React
pnpm add -D @testing-library/react @testing-library/jest-dom jsdom
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
include: ["src/**/*.{test,spec}.{ts,tsx}"],
coverage: {
provider: "v8",
reporter: ["text", "json", "html"],
exclude: ["node_modules/", "src/test/"],
},
},
});
Playwright Setup
# Install
pnpm add -D @playwright/test
npx playwright install
// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./e2e",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: "html",
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
},
projects: [
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
{ name: "firefox", use: { ...devices["Desktop Firefox"] } },
{ name: "webkit", use: { ...devices["Desktop Safari"] } },
],
webServer: {
command: "pnpm dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
Test Patterns
Unit Test Example
// utils/format.test.ts
import { describe, it, expect } from "vitest";
import { formatCurrency, formatDate } from "./format";
describe("formatCurrency", () => {
it("formats USD correctly", () => {
expect(formatCurrency(1234.56, "USD")).toBe("$1,234.56");
});
it("handles zero", () => {
expect(formatCurrency(0, "USD")).toBe("$0.00");
});
it("handles negative values", () => {
expect(formatCurrency(-50, "USD")).toBe("-$50.00");
});
});
Component Test Example
// components/Button.test.tsx
import { render, screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import { Button } from "./Button";
describe("Button", () => {
it("renders with text", () => {
render(<Button>Click me</Button>);
expect(screen.getByRole("button")).toHaveTextContent("Click me");
});
it("calls onClick when clicked", () => {
const handleClick = vi.fn();
render(<Button
fireEvent.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("is disabled when loading", () => {
render(<Button loading>Submit</Button>);
expect(screen.getByRole("button")).toBeDisabled();
});
});
E2E Test Example
// e2e/auth.spec.ts
import { test, expect } from "@playwright/test";
test.describe("Authentication", () => {
test("user can login", async ({ page }) => {
await page.goto("/login");
await page.fill('[name="email"]', "user@example.com");
await page.fill('[name="password"]', "password123");
await page.click('button[type="submit"]');
await expect(page).toHaveURL("/dashboard");
await expect(page.locator("h1")).toContainText("Welcome");
});
test("shows error for invalid credentials", async ({ page }) => {
await page.goto("/login");
await page.fill('[name="email"]', "wrong@example.com");
await page.fill('[name="password"]', "wrongpass");
await page.click('button[type="submit"]');
await expect(page.locator('[role="alert"]')).toContainText("Invalid");
});
});
Testing Strategy Guide
| Test Type | Coverage | Speed | Confidence |
|---|---|---|---|
| Unit | High (70-80%) | Fast | Low-Medium |
| Integration | Medium (15-20%) | Medium | Medium-High |
| E2E | Low (5-10%) | Slow | High |
Reference Files
- Vitest Config: See references/vitest-config.md
- Playwright Patterns: See references/playwright-patterns.md
- Testing Best Practices: See references/testing-best-practices.md
- Mocking Strategies: See references/mocking.md
Best Practices
- Test behavior, not implementation: Focus on what, not how
- Arrange-Act-Assert: Clear test structure
- One assertion per test: When possible
- Descriptive names:
should return empty array when no items - Avoid test interdependence: Each test should be isolated
- Mock external dependencies: APIs, databases, time
- Use factories: For test data generation
- Run tests in CI: Every PR, every push