RizTech Academy logo
RizTech Academy
Testing the Whole StackLesson 1 of 525 min

What to test, and what not to bother with

Most advice about testing is either "test everything" or a number — eighty percent coverage, the test pyramid, one assertion per test. None of it survives contact with a real project, because none of it answers the only question that matters: which tests earn their keep?

A test costs you three times. Once to write it. Every time it runs, in time you wait. And every time the code changes and the test has to change with it — which is the expensive one, and the one nobody counts.

The test that is worth writing

Ask two questions about any test you are about to write.

If this broke, would anybody notice? Not "is it correct" — would a customer, or the shopkeeper, or the accountant notice? If cartTotals is wrong, somebody is billed a number they were never shown. That is worth a test. If a heading is text-lg instead of text-xl, nobody will ever ring the shop about it.

Could this break without the code changing? A pure function only breaks when you edit it. Anything that depends on the database, on time, on concurrency or on another process can break while you sleep — a migration, a timezone, a second customer. Those deserve tests at a level where the dependency is real.

Together they explain why this course tests money, stock, the state machine and the dates very hard, and tests the layout not at all.

What each layer can prove

The layers are not a hierarchy of goodness. Each one can prove something the others cannot, and is wasted on everything else.

Layer What only it can prove Cost
Unit arithmetic, rules, transformations milliseconds
Service branches and refusals, with fakes milliseconds
API end-to-end transactions, locks, cookies, signatures seconds, needs PostgreSQL
Component what a person actually sees rendered milliseconds
Browser (Playwright) the whole thing joined up tens of seconds, needs everything

The Kirana Store ends up with 124 tests, and the shape is lopsided on purpose: 110 of them run in under a second, 29 need a database, and 7 need a browser.

That is the pyramid, but arrived at by asking what each test proves rather than by obeying a diagram.

Do not test the same thing twice

This is the rule that keeps a suite from rotting.

cartTotals is unit tested exhaustively — every boundary of the free-delivery threshold, an empty cart, a hundred lines. So the API end-to-end test does not re-check the arithmetic. It checks that the order's total matches the cart's, which is a different claim: that the two halves agree.

And the Playwright test checks neither. It checks that somebody can buy something.

When a test fails, you should be able to tell from which test it was what broke. Three tests covering the same ground all go red together and tell you nothing.

The tests this course does not write

Worth listing, because leaving them out is a decision.

No snapshot tests. A snapshot asserts "the output is what it was", which is not a claim about correctness. In practice somebody changes the markup, the snapshot fails, they run --update without reading it, and the suite has learned nothing while costing everybody time.

No tests of Prisma. findMany works. Testing that a query builder builds queries is testing somebody else's library.

No tests that mock the thing under test. If a service test mocks Prisma and then asserts expect(prisma.variant.updateMany).toHaveBeenCalled(), it passes when the SQL is wrong, when the condition is wrong, and when the database rejects it. It tests the mock.

That last one is why stock release is proved end to end in this course, against a real PostgreSQL, and not in a unit test.

No coverage target. Coverage tells you which lines ran, not whether anything was checked. A test that calls every function and asserts nothing scores 100%. It is a useful map of what is untested; it is a terrible goal.

The one number that matters: how long it takes

npm run test        →  110 tests, about 4 seconds
npm run test:e2e    →   29 tests, about 3 seconds, needs PostgreSQL
playwright test     →   14 runs,  about 15 seconds, needs everything

A suite you run is worth ten times a suite you do not. The moment the fast tests take a minute, people stop running them before pushing, and the whole thing becomes CI's problem — which means finding out twenty minutes later instead of immediately.

So the split is by what it needs, not by what it is called. npm run test needs nothing and runs on every save. Everything else runs before a push and in CI.

A red test must mean something

The worst outcome in testing is not an untested bug. It is a suite nobody trusts.

A flaky test — one that fails sometimes for reasons unrelated to the code — teaches everybody to re-run the build. Once that habit exists, a real failure is re-run too, and the suite has become worse than useless: it costs time and catches nothing.

So: a flaky test is a bug in the test, and it gets fixed or deleted the same day. There is no third option. The next lessons show two real ones from this project — an ambiguous selector and a race after a sign-in — and neither was the application's fault.

What to write first on a project that has none

In order:

  1. The money. Whatever computes what somebody is charged.
  2. The rules. The state machine, the permissions, what is allowed.
  3. One end-to-end path through the thing the business exists to do. For a shop, buying something.
  4. A test for each bug you fix, from then on.

Number four is the one that compounds. Every bug you fix is proof that you can be wrong about that thing, and a test there is a test aimed at somewhere you have already been caught out.

Check your work

The three costs of a test: writing it, running it, and changing it when the code changes — the last being the expensive one.

The two questions: would anybody notice if this broke, and could it break without the code changing?

Why the layers are not a hierarchy: each proves something the others cannot, and is wasted on the rest.

Why not to test the same thing twice: a failure should tell you what broke, and duplicated coverage goes red together saying nothing.

Why no snapshot tests: they assert that the output is unchanged, not that it is right, and updating them is a reflex.

Why mocking Prisma and asserting the call proves nothing: it passes with wrong SQL, a wrong condition and a rejecting database.

Why no coverage target: a test that asserts nothing scores 100%. Coverage maps what is untested; it is not a goal.

Why speed matters: a suite people stop running before pushing has become CI's problem, and CI answers twenty minutes late.

Why a flaky test is an emergency: it teaches everybody to re-run the build, and then real failures get re-run too.

What to write first: the money, the rules, one end-to-end path, and then a test for every bug you fix.

Practice

  1. Open the Kirana Store's test folders and count how many tests need a database. Explain why each one does.
  2. Pick a test in packages/shared and work out what would have to break for it to fail. Then do the same for a Playwright test.
  3. Find a function in the project you would not test, and write down why.
  4. Delete an assertion from cartTotals's tests and see whether anything else catches it.
  5. Run npm run test:cov on the API. Find a file with high coverage and decide whether it is actually well tested.
  6. Time npm run test. Decide what number would make you stop running it before every push.
  7. Write down the last bug you fixed in any project, and the test you would add for it.
  8. Find a test in an open-source project that asserts a mock was called. Decide what it would catch.
  9. Take one end-to-end test from this project and argue for deleting it. Then argue against.
  10. Write the four things you would test first on a project you have never seen.

Next: unit testing the services, without a dependency injection container in sight.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship