Testing Strategies - The Test Pyramid for Quality Assurance

16 min read | 2025.12.01

Why Testing is Important

Testing is an important means of ensuring code quality and preventing regressions.

Code without tests:

  • Makes refactoring scary
  • Makes it unclear what changes affect
  • Leads to bugs being discovered in production

The Test Pyramid

A model showing types of tests and their recommended ratios.

flowchart TB
    subgraph Pyramid["Test Pyramid"]
        E2E["E2E (10%)<br/>High cost, slow, unstable"]
        Integration["Integration (20%)<br/>Medium cost, medium speed"]
        Unit["Unit Tests (70%)<br/>Low cost, fast, stable"]
        E2E --> Integration --> Unit
    end

Unit Tests

Testing individual functions or classes in isolation.

Characteristics

ItemUnit Test
TargetFunctions, classes, modules
SpeedVery fast (ms)
StabilityHigh
CoverageNarrow (single function)

Implementation Example

// Test target
function calculateTotal(items, taxRate) {
  const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  return Math.round(subtotal * (1 + taxRate));
}

// Test
describe('calculateTotal', () => {
  it('calculates total amount for products', () => {
    const items = [
      { price: 100, quantity: 2 },
      { price: 200, quantity: 1 }
    ];
    expect(calculateTotal(items, 0.1)).toBe(440);
  });

  it('returns 0 for empty array', () => {
    expect(calculateTotal([], 0.1)).toBe(0);
  });

  it('returns subtotal when tax rate is 0%', () => {
    const items = [{ price: 100, quantity: 1 }];
    expect(calculateTotal(items, 0)).toBe(100);
  });
});

AAA Pattern

it('creates a user', () => {
  // Arrange
  const userData = { name: 'Alice', email: 'alice@example.com' };

  // Act
  const user = createUser(userData);

  // Assert
  expect(user.id).toBeDefined();
  expect(user.name).toBe('Alice');
});

Integration Tests

Testing that multiple components work together.

Characteristics

ItemIntegration Test
TargetAPI, database integration, external services
SpeedModerate (seconds)
StabilityModerate
CoverageModerate

Implementation Example (API)

describe('POST /api/users', () => {
  beforeEach(async () => {
    await db.users.deleteMany();
  });

  it('creates a new user', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ name: 'Alice', email: 'alice@example.com' })
      .expect(201);

    expect(response.body.id).toBeDefined();
    expect(response.body.name).toBe('Alice');

    // Confirm saved to DB
    const user = await db.users.findById(response.body.id);
    expect(user).not.toBeNull();
  });

  it('returns error for duplicate email address', async () => {
    await db.users.create({ name: 'Bob', email: 'alice@example.com' });

    await request(app)
      .post('/api/users')
      .send({ name: 'Alice', email: 'alice@example.com' })
      .expect(409);
  });
});

E2E Tests (End-to-End Tests)

Testing the entire application from the user’s perspective.

Characteristics

ItemE2E Test
TargetEntire user flow
SpeedSlow (minutes)
StabilityLow (prone to flakiness)
CoverageWide

Implementation Example (Playwright)

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

test('login and display dashboard', async ({ page }) => {
  // Navigate to login page
  await page.goto('/login');

  // Fill form
  await page.fill('[name="email"]', 'user@example.com');
  await page.fill('[name="password"]', 'password123');
  await page.click('button[type="submit"]');

  // Confirm redirect to dashboard
  await expect(page).toHaveURL('/dashboard');
  await expect(page.locator('h1')).toContainText('Dashboard');
});

test('search and purchase product', async ({ page }) => {
  await page.goto('/');

  // Search
  await page.fill('[name="search"]', 'laptop');
  await page.click('button[type="submit"]');

  // Add product to cart
  await page.click('[data-testid="add-to-cart"]');

  // Go to cart
  await page.click('[data-testid="cart-icon"]');
  await expect(page.locator('.cart-item')).toHaveCount(1);
});

Mocks/Stubs

Simulating external dependencies to isolate tests.

// Mock external API
jest.mock('./paymentService', () => ({
  processPayment: jest.fn().mockResolvedValue({ success: true, transactionId: 'tx_123' })
}));

import { processPayment } from './paymentService';

it('processes payment', async () => {
  const result = await checkout(order);

  expect(processPayment).toHaveBeenCalledWith({
    amount: order.total,
    currency: 'JPY'
  });
  expect(result.transactionId).toBe('tx_123');
});

Test-Driven Development (TDD)

A development method where you write tests before implementation.

flowchart LR
    Red["Red<br/>(Failing test)"] --> Green["Green<br/>(Minimal implementation)"] --> Refactor["Refactor<br/>(Improve code)"] --> Red

TDD Example

// 1. Red: Write a failing test
it('throws error when password is less than 8 characters', () => {
  expect(() => validatePassword('1234567')).toThrow('Password too short');
});

// 2. Green: Minimal implementation
function validatePassword(password) {
  if (password.length < 8) {
    throw new Error('Password too short');
  }
}

// 3. Refactor: Improve code as needed

Coverage

A metric showing how much of your code is covered by tests.

Coverage TypeDescription
Line coveragePercentage of lines executed
Branch coveragePercentage of branches executed
Function coveragePercentage of functions executed

Coverage Guidelines

80% is often the target

Consideration
No need to aim for 100%
High coverage ≠ high test quality
Prioritize covering important paths

Choosing a Testing Strategy

ScenarioEmphasized Test
Complex business logicUnit tests
Many external integrationsIntegration tests
UI is importantE2E tests
Legacy code refactoringE2E as safety net

Summary

An effective testing strategy is about balancing the test pyramid and distributing each level of tests appropriately. Focus on unit tests, verify integrations with integration tests, and guarantee important user flows with E2E tests. Testing is not something that slows down development, but an investment that improves long-term quality and development efficiency.

← Back to list