Unit, integration, and E2E testing: what each level tests, recommended ratios, tooling recommendations, and cost/benefit analysis.
Unit, integration, and E2E testing: what each level tests, recommended ratios, tooling recommendations, and cost/benefit analysis.
BeforeMerge offers hundreds of code review rules, guides, and detection patterns to help your team ship better code.
The testing pyramid provides a framework for how many tests to write at each level. More unit tests at the base, fewer E2E tests at the top.
/ E2E \ ~10% — Slow, expensive, high confidence
/----------\
/ Integration \ ~20% — Medium speed, tests boundaries
/----------------\
/ Unit Tests \ ~70% — Fast, cheap, isolated
/____________________\What they test: Individual functions, classes, or components in isolation.
Characteristics:
// Pure function — ideal for unit testing
function calculateDiscount(price: number, tier: "basic" | "premium"): number {
return tier === "premium" ? price * 0.2 : price * 0.1;
}
describe("calculateDiscount", () => {
it("applies 20% discount for premium tier", () => {
expect(calculateDiscount(100, "premium")).toBe(20);
});
it("applies 10% discount for basic tier", () => {
expect(calculateDiscount(100, "basic")).toBe(10);
});
});Tools: Vitest, Jest
What they test: How multiple units work together, including real external services.
Characteristics:
describe("POST /api/users", () => {
it("creates a user and returns 201", async () => {
const res = await request(app)
.post("/api/users")
.send({ name: "Alice", email: "alice@test.com" });
expect(res.status).toBe(201);
expect(res.body.user.email).toBe("alice@test.com");
// Verify it actually hit the database
const user = await db.user.findUnique({ where: { email: "alice@test.com" } });
expect(user).not.toBeNull();
});
});Tools: Vitest + Supertest, Playwright API testing
What they test: Complete user flows through the real application.
Characteristics:
test("user can sign up and create a post", async ({ page }) => {
await page.goto("/signup");
await page.fill("[name=email]", "test@example.com");
await page.fill("[name=password]", "secure123");
await page.click("button[type=submit]");
await expect(page).toHaveURL("/dashboard");
await page.click("text=New Post");
await page.fill("[name=title]", "My First Post");
await page.click("text=Publish");
await expect(page.locator(".success-toast")).toBeVisible();
});Tools: Playwright, Cypress
| Level | Speed | Maintenance | Confidence | Coverage |
|---|---|---|---|---|
| Unit | Fast | Low | Logic correctness | Narrow |
| Integration | Medium | Medium | System boundaries | Medium |
| E2E | Slow | High | User experience | Broad |