Should I test that?

Should I write tests before code?

Answer

Yes, write the test before the code whenever you can state the expected result in advance, such as a rule from a ticket or the input from a bug report, and see the test fail before you write the code.

Verdict on the code under testYes

Why

Yes, write the test first whenever you can state the expected result before the code exists. For my typical case, a feature in a web application that customers use, rule R11 gives Test, so the test gets written either way and the order decides where its expected values come from. Blast radius is users, Change frequency is regularly, and Detectability is eventually, since a rule that returns a plausible wrong value raises no error. Reversibility is with-effort, because records saved by a wrong rule need a repair script, and Test cost is moderate, about an hour per behaviour. I write the test first because a test that fails before the code exists has proved it can fail.

When the decision changes
WhenDecisionWhy
The feature computes invoice totals, discounts or refundsTest mandatory: write tests for each amount and boundary first, reviewed by a second personBlast radius rises to money and Reversibility to costly, because a wrong charge ends in refunds
The code decides which account may read which recordsTest mandatory: before adding the check, write a test that another account's request is denied, and see it failBlast radius rises to safety-or-legal and Detectability to never, because a leaked record raises no error
You do not know yet what a new module should return, and its functions change several times a weekTest: write the code first, then add tests in the same pull request and break the code once to see each failChange frequency rises to constantly, and Detectability stays eventually, so rule R11 still gives Test; tests written before the functions settle get rewritten
The code is a prototype that only you runDo not write tests for the prototype until someone else uses itBlast radius falls to none, because you alone bear a failure
The change renames a field, and the type checker rejects every caller that uses the old nameDo not write a test for the rename; the type check fails on every caller you missedDetectability falls to immediately and Reversibility to trivial, because the build fails before the merge
The code ranks search results, and nobody can write down the right order before codingTest the ranking differently: alert when clicks on the top three results of real searches dropTest cost rises to heavy, because no exact expected output exists, while Detectability stays eventually and Reversibility with-effort

What breaks if you don't test

Without a test, a later change breaks a rule nobody checks by hand again, and customers find wrong values weeks later. A test written after the code fails differently: the developer copies the function's output into the expected value, and the test locks in what the code already does.

What you lose if you over-test

Tests written first for a spike that changes its functions three times in a week get rewritten three times. Test-first for every internal class ties the suite to one design, so a refactor fails tests while the behaviour stays right.

How to test

Test the rule at the level where it shows, usually a unit test in CI:

  1. Turn each rule in the ticket into a test with input and expected output from the ticket, following the red, green, refactor cycle.
  2. Run it and check that it fails for the expected reason, such as a wrong date.
  3. Write the code until the test passes, then refactor.
  4. If you wrote the code first, still take expected values from the ticket, then break the code once and check that the test fails.

Step size matters more than order: in a study of 39 professional developers, Fucci et al. found that writing the test or the code first had no important effect on quality or productivity, while small, even steps did.

When the answer changes

  • The code charges money or decides who may read which records.
  • You do not know yet what the code should return.
  • The right output is a judgement, such as a search ranking.

Real incident + Code example

The delivery date the test agreed with

On an online shop I worked on, a developer wrote deliveryDate() for the promise "delivered in 2 working days", ran it, and pasted its result into the test. The function counted Saturday as a working day, so a Thursday order on 5 March showed Saturday 7 March, and the test asserted that date. CI stayed green. Late-parcel tickets came in for three weeks before anyone traced them. The fix started from the ticket's cases, written before the code:

import { describe, expect, it } from "vitest";
import { deliveryDate } from "./delivery";

// Cases from the ticket: 2 working days, weekends excluded
describe("deliveryDate", () => {
  it("counts two working days from a Monday", () => {
    expect(deliveryDate("2026-03-02")).toBe("2026-03-04");
  });

  it("skips the weekend for a Thursday order", () => {
    expect(deliveryDate("2026-03-05")).toBe("2026-03-09");
  });

  it("skips the weekend for a Friday order", () => {
    expect(deliveryDate("2026-03-06")).toBe("2026-03-10");
  });
});

The old function fails the Thursday and Friday cases.

FAQ

Should unit tests be written before the code is written?

Write unit tests before the code when the expected result is known, such as a rule in a ticket, and run each one to see it fail. When the expected result is still unknown, write the code first and its tests in the same pull request.

Does the order of writing tests and code matter?

The order of writing tests and code had no important effect on quality or productivity in a study of 39 professional developers by Fucci et al., while working in small, even steps did. A test written after the code can still copy the code's output and pass on the bug.

How do I check that a test written after the code works?

Break the code once, for example by flipping a comparison, check that the test fails, then restore the code. Mutation testing tools such as StrykerJS and PIT make such changes automatically and report each one no test caught.