Writing Tests
Tests are code that runs your code and checks the result. Without them, every change is a gamble. With them, you can refactor confidently and catch regressions before users do.
The Test Pyramid
Tests come in three layers:
- Unit tests — exercise a single function or class in isolation. Fast (milliseconds), focused, and the bulk of your test suite.
- Integration tests — verify that multiple components work together correctly: a function that hits the database, an API endpoint that uses several services. Slower but cover the seams unit tests miss.
- End-to-end (E2E) tests — drive the whole app like a real user, often through a browser. Slowest and flakiest, so keep them few but high-value.
The pyramid: many unit tests, fewer integration tests, even fewer E2E tests.
The AAA Pattern
A well-written test has three blocks, in this order:
- Arrange — set up the data and dependencies the test needs
- Act — call the code you are testing, exactly once
- Assert — verify the result matches expectations
Keeping the structure consistent makes tests easy to scan, and it makes failures easy to diagnose. The example shows AAA explicitly so you can see the shape.
What to Test
Cover three categories:
- Happy path — the typical, correct usage
- Edge cases — empty inputs, zero, negative numbers, very large values, Unicode
- Error cases — invalid input should fail with a clear, predictable error
The third category is the one most often missed; the example tests that divide(10, 0) throws.
Test Quality Signals
A good test is independent (does not depend on other tests' state), fast, deterministic (same result every run), and meaningful (failing tells you something specific went wrong). If a test is flaky, fix it or delete it — flaky tests train your team to ignore failures.
Try It Yourself
- Add a test that checks
add(0, 0) == 0 - Write a test for a
reverse(str)function and include an empty string case - Refactor a test you find online to follow AAA explicitly