Tests that write themselves into the future

~8 min

Here’s a fact that quietly restructures how you picture engineering. In a healthy codebase, often a third or more of the code isn’t the product at all. It’s code that checks the product.

Tests are Module 3’s unhappy-path discipline made permanent. Instead of a human verifying the delivery fee once, a test verifies it forever, on every future change, in milliseconds. Tests come in sizes, and the sizes have names.

What is a unit test?

A Concept · lights on your mapunit testingThe smallest kind of test. Give a single function specific inputs and confirm that it returns exactly what it should. Cheap enough to have thousands, fast enough to run constantly, and when one fails it points at the exact line to blame. checks one small piece, usually one function, in isolation. Remember delivery_fee from Module 3? Its test is four lines you can read cold:

A real unit test — for a function you already knowPython
1def test_members_get_free_delivery():
Test functions are named as sentences, so failures read like news headlines.
2assert delivery_fee(19.00, True) == 0
assert = “this must be true, or fail loudly.” A member's fee must be zero…
3assert delivery_fee(19.00, False) == 4.99
…a non-member under $25 pays standard…
4assert delivery_fee(25.00, False) == 0
…and exactly $25.00 rides free. Note the value chosen: the boundary. Tests live at the edges (Module 11 trained you why).

Read one line to learn the pattern. delivery_fee(19.00, True) == 0 asks the function about a $19 order from a member (that is what the True means) and insists the answer be zero. The other two lines ask the same question about a non-member, and about the $25 boundary.

That is the whole mechanic. Call the function with known inputs, assert what must come back. When the rule breaks someday, the failure names its culprit precisely: test_members_get_free_delivery FAILED, expected 0, got 4.99. A codebase has thousands of these, and they all run in seconds.

What is an integration test?

Now a trap unit tests cannot catch. Suppose delivery_fee is perfect, and the payment-charging code is perfect too, but one returns cents while the other expects dollars. Every unit test passes, and every customer gets charged a hundred times too much.

The bug lives in neither part. It lives in the seam, and that is what Concept · lights on your mapintegration testingTests that wire two or more parts up and run them as a pair, so a mismatch at the handoff (cents in, dollars expected) gets caught. Costlier than unit tests, hence fewer, each pointed at a place where parts meet. exist to check: several components working together. An integration test makes checkout actually write to a real (test) database and actually call the payment code, then checks that the numbers survived the handoff. These tests run slower than unit tests because real databases take real seconds, so teams keep fewer of them and aim them at the seams that matter.

What is an end-to-end test?

At the top, Concept · lights on your mapend-to-end testingThe full-journey test: a scripted browser opens the real app, clicks through an order, and checks the confirmation page appears. Truest to reality and slowest to run, so a team keeps just a few, guarding the paths customers cannot lose. drive the entire product exactly the way a user would, by robot. An end-to-end test is a script for a browser with no human at the keyboard:

A robot orders a latteE2E
1open the coffee app
A real browser launches, and nobody is touching it.
2click “Add latte” → click “Checkout”
3pay with the fake test card
Real screens, real clicks, fake money.
4assert the page says “Order confirmed”
If this text never appears, the test fails, and something in the whole chain broke.

This is the realest test there is, exercising the browser, the API, the database, everything at once. And it is the slowest, taking minutes rather than milliseconds. So teams keep only a precious handful, covering the journeys that must never break: ordering, paying, signing in.

Why a pyramid?

Put the three sizes together and the industry’s standard shape appears:

The test pyramid
End-to-end
a handful · minutes each · journeys only
Integration
dozens · seconds each · the seams between components
Unit tests
thousands · milliseconds each · most bugs caught here, cheapest
Fig. 2 — wide base, narrow peak

The shape is economics. Cost, slowness, and fragility all rise as you climb, so you want many cheap precise checks at the bottom and few expensive realistic ones at the top. A team whose pyramid is upside down (hundreds of slow browser tests, few unit tests) waits an hour to learn what a millisecond test would have said.

Automated versus manual

Everything above shares one property. It runs by machine. That is the first half of a pair. The whole pyramid is Concept · lights on your mapautomated testingTesting done by programs rather than people: the same verification repeated exactly, on every change, without fatigue. Everything in the pyramid is this kind; the opposite is a person clicking through the product by hand., checks written as code, run identically, thousands of times a day, never bored on the four-hundredth checkout.

Concept · lights on your mapmanual testingA person trying the product themselves and noticing what feels wrong. Slow and hard to repeat, but the only kind of testing that catches problems no assertion was ever written for., a human actually using the product, still matters where judgment does. A machine can assert that the confirmation appears. It cannot notice that the new checkout feels confusing, that the button is technically present but nobody would find it, that something is off in a way no one thought to write a check for. The UX quality Module 5 was about is something an assertion cannot capture.

The modern division of labor comes down to this. Automate everything repeatable, and spend scarce human attention where machines are blind (exploration, judgment, weirdness).

Engineers defend test-writing time fiercely because the tests are a safety net for the future. When someone refactors the ordering code next year (Module 11’s debt payment), four thousand green checkmarks are what makes “behavior unchanged” a verified fact instead of a hope. Tests are how a codebase lets strangers change it safely, and next lesson’s bots will run them on every single PR.

The mental model to remember

A unit test checks one function in isolation. Known inputs, asserted outputs, millisecond speed, precise blame. Thousands of them form the pyramid’s base.

An integration test checks components working together, probing the seams where bugs hide between perfectly correct parts.

An end-to-end test is a robot using the real product. It is the truest check and the slowest, so only the vital journeys get one.

The pyramid’s shape is economics. Many cheap checks sit below, few expensive ones above. All of it is automated; manual testing spends human judgment on what machines can’t name.

You should now be able to hear “the unit tests pass but an integration test is failing” and know exactly what kind of bug that implies. The parts are fine, and something is wrong in a seam.

Check — then the lesson continues

A refactor of the pricing module accidentally breaks the “exactly $25 rides free” rule. With the pyramid in place, how does the team find out?

▼ answer the check to continue ▼