D
The Dev Blog
testing architecture backend

A Pragmatic Testing Strategy for Real Projects

M
Mohan
· · 6 min read
Share
A Pragmatic Testing Strategy for Real Projects

Most testing advice is either too academic or too dogmatic. The testing pyramid tells you to write lots of unit tests, fewer integration tests, and even fewer end-to-end tests.

In practice? Integration tests give you the most confidence per line of test code.

The Testing Trophy

Kent C. Dodds proposed the “testing trophy” as a better model:

  1. Static analysis (TypeScript, ESLint) - catches typos and obvious errors
  2. Unit tests - for pure logic and algorithms
  3. Integration tests - test features as users experience them
  4. E2E tests - critical user paths only

The trophy shape puts integration tests at the widest point. That’s where the most value is.

What to Unit Test

Unit test things that are:

  • Pure functions with complex logic
  • Algorithms that have edge cases
  • Utility functions used across the codebase
// Worth unit testing - complex logic, many edge cases
function parseTimeRange(input: string): { start: Date; end: Date } {
  // ...
}

// Not worth unit testing - just plumbing
function getUserById(id: string) {
  return db.users.findUnique({ where: { id } });
}

Don’t unit test wrappers, simple CRUD operations, or framework code.

What to Integration Test

Integration tests should exercise real features through their public interface:

describe('User registration', () => {
  it('creates a user and sends a welcome email', async () => {
    const response = await request(app)
      .post('/api/register')
      .send({ email: 'test@example.com', password: 'secure123' });

    expect(response.status).toBe(201);

    const user = await db.users.findByEmail('test@example.com');
    expect(user).toBeDefined();

    expect(emailService.sent).toContainEqual(
      expect.objectContaining({ to: 'test@example.com', template: 'welcome' })
    );
  });
});

This single test covers the API endpoint, validation, database persistence, and email sending. One test, high confidence.

What to E2E Test

Only test critical business paths:

  • User signup and login
  • Core product workflow (checkout, payment, etc.)
  • Permission boundaries (admin vs. user)

E2E tests are slow and flaky. Keep them focused on the paths where a failure means lost revenue or users.

The Pragmatic Rules

  1. If a bug reaches production, write a test that would have caught it. This naturally builds coverage where it matters most.

  2. Delete tests that break with every refactor but catch no bugs. They’re maintenance cost with no value.

  3. Mock at the boundary, not in the middle. Mock the database driver, not your repository layer.

  4. Test behavior, not implementation. “When a user submits the form, they see a success message” - not “when handleSubmit is called, setState is invoked with {loading: true}.”

The goal isn’t 100% coverage. It’s maximum confidence with minimum maintenance burden.

M

Mohan

Software engineer writing about AI, distributed systems, and the craft of building great software.

Share

Stay up to date

Get notified when I publish new articles. No spam, unsubscribe anytime.

No spam. Unsubscribe anytime.