Testing Fundamentals

Why Testing Matters

Understand the real cost of untested code and why testing is more important in the AI era, not less.

The Real Cost of Bugs

A bug caught during development costs minutes to fix. The same bug caught in staging costs hours. Caught by a user in production? Days — and real damage to trust and revenue.

Testing is an investment, not overhead. Teams that invest in testing ship faster over time, not slower.

The Testing Pyramid

The testing pyramid describes the optimal distribution of test types:

Unit Tests (base, many) — Fast, cheap, isolated. Test a single function or component. Run in milliseconds. Write hundreds of them.

Integration Tests (middle, fewer) — Test how components work together. More realistic than unit tests, slower. Write dozens.

E2E Tests (apex, few) — Test the entire application from the user's perspective. Slow, expensive to maintain. Write for critical flows only.

The pyramid shape matters: most tests should be fast unit tests, fewer slow E2E tests. Inverting the pyramid (too many E2E tests) makes your test suite slow, flaky, and expensive.

What Testing Gives You

Confidence to refactor. When tests cover your code, you can restructure it without fear. The tests verify you didn't break anything.

Confidence to deploy. When CI runs tests on every push, you know production-bound code has been verified.

Living documentation. Well-named tests explain what the code is supposed to do. it('returns 401 when token is expired') tells the next developer exactly what happens.

Faster onboarding. New developers read tests to understand how the system behaves — no need to reverse-engineer the implementation.

Testing in the AI Era

AI coding tools (Claude Code, Cursor, Copilot) generate code faster than any human can write. This has an important consequence: bugs can be generated faster than ever.

AI generates plausible-looking code. It compiles. It runs. But does it behave correctly for edge cases? Does it handle errors? Does it enforce security constraints?

Testing is MORE important in the AI era, not less. The velocity gain from AI tools is only safe when tests catch the defects AI introduces.

The powerful combination: human writes the spec → AI generates the implementation → tests verify the output.

How Testing Connects to Specifications

When you write clear requirements before implementation, tests become obvious. Each requirement maps directly to a test assertion:

RequirementTest
"Password must be at least 8 characters"expect(validate('short')).toBe(false)
"Returns 401 when not authenticated"expect(response.status).toBe(401)
"Sends welcome email after registration"expect(emailService.send).toHaveBeenCalled()

The tighter your specifications, the easier your tests are to write.

Key Takeaways

  • A bug in production costs 10-100x more to fix than one caught during development — testing pays for itself
  • The testing pyramid: many unit tests, fewer integration tests, few E2E tests — fast tests at the base
  • Testing gives confidence to refactor, to deploy, and to let AI generate code
  • AI tools generate code faster, which means bugs propagate faster — testing is the counterbalance
  • Well-written test names serve as living documentation of system behavior

Example

typescript
// Tests as living documentation
describe('PasswordValidator', () => {
  it('rejects passwords shorter than 8 characters', () => {
    expect(validate('short')).toBe(false);
  });

  it('requires at least one uppercase letter', () => {
    expect(validate('alllowercase1')).toBe(false);
  });

  it('requires at least one number', () => {
    expect(validate('NoNumbers!')).toBe(false);
  });

  it('accepts a password meeting all requirements', () => {
    expect(validate('SecurePass1!')).toBe(true);
  });
});
// Anyone reading these tests knows exactly what PasswordValidator does
Try it yourself — TYPESCRIPT