Tests: how to change code without fear
A test is a claim about your code, written down so a machine can check it in a second. It is what turns “I think this still works” into “I know this still works”.
It is 23:00. You have to change how the shopping cart calculates a discount. The function is forty lines long, it was written by someone who left the company, and it feeds the price on every page of the site. You change two lines and now you have to find out whether you broke anything. There are two ways to live: open the site, click through every page by hand and hope you did not miss one, or run a command and get an answer in two seconds. The second one is not magic. It is tests.
A test is a claim written down in a form a machine can check. You say: for these inputs, this function must give this answer. Then you run it. If the answer matches, the claim held. If it does not, you just learned something. A test is not a proof that the code is correct — it is a list of specific things you promised, checked automatically, every time.
vat(net, rate) = net * (1 + rate)
assert priceWithVat(100, 0.21) === 121
assert priceWithVat(0, 0.21) === 0
assert priceWithVat(100, 0) === 100
assert x == y
FAIL at line 6Not all tests are the same size. A unit test takes one small piece of code — usually one function — and feeds it inputs, with nothing else involved: no network, no database, no browser. It runs in a fraction of a second, and when it fails you know exactly which function is broken. An integration test runs several real pieces together: your code plus a real database, or your code talking to another service over the network. It is slower, it can fail for reasons that have nothing to do with your change, and it catches a completely different kind of mistake — the kind that only appears once the pieces are wired together. You need both, and you need to know which one you are looking at.
This is the part that surprises people: a failing test is good news. A red test means the machine found a difference between what you promised and what the code does, and it found it now, on your laptop, in two seconds — instead of in front of a customer next Thursday. The uncomfortable version of the same bug is the one nobody tested for: it reaches real people, and you find out from a message that starts with “hey, is the site broken for you too?”
The real reason teams write tests has nothing to do with finding bugs. It is about fear. In a codebase with no tests, every change is a gamble, so people make the smallest possible changes and avoid touching anything that works — including the ugly parts that desperately need rewriting. With tests, you can change a function, run the suite, and know in seconds whether you broke the rest. That is what makes cleaning up and improving code possible at all. Tests are not a report you hand to a teacher. They are the thing that lets you move.
Practice
Match each check with what it really is.
Hints
- Ask what each check needs in order to run: only this one function, or a whole running system?
- The slower and more realistic a check is, the more it costs to run and the more it can tell you about the real thing.
15 XP
You wrote a test, ran it, and it failed. What is the most useful next move?
Hints
- Ask who is wrong when the check fails: the code, or the expectation you wrote down?
- A red test is new information. Ask what it just told you, not how to make it quiet.
14 XP
Put the order in which a careful developer kills a bug for good.
Hints
- You cannot prove a fix works until you have watched the bug happen on purpose.
- The old test stays in the suite forever: that is what stops the same bug from coming back.
16 XP