Should I test that?

Should I use TDD?

Answer

Yes, use TDD for business rules and calculations whose correct result you can state before you write the code: write one failing test from the requirement, write the code that makes it pass, refactor, and keep the tests in CI.

Verdict on the code under testYes

Why

Yes, use TDD on code the framework says to test. My typical case is a developer adding a business rule to a web backend, such as the date a support ticket escalates. Blast radius is users and Change frequency is regularly, because customers see the result and most features touch a rule. Detectability is eventually, because a wrong date looks plausible and raises no error. Reversibility is with-effort, since stored results need a repair script, and Test cost is moderate, about an hour per behaviour, so rule R11 gives Test. Writing the test before the code costs the same hour and takes the expected value from the requirement instead of the code's output.

When the decision changes
WhenDecisionWhy
The code is a spike to try a library, which only you run and then deleteDo not test the spike; start TDD when you rebuild the code for productionBlast radius falls to none, because only you run the spike and bear a failure
The rule calculates charges, discounts or invoice totalsTest mandatory: write a failing test for each amount and boundary before the code, and have a second person review the testsBlast radius rises to money and Reversibility to costly, because a wrong charge ends in refunds
The rule decides which users may read a recordTest mandatory: write allowed, denied and cross-account tests before the codeBlast radius rises to safety-or-legal and Detectability to never, because a leaked record raises no error
The change is a colour, a margin or a label that the developer sees on screen right after the changeDo not test: skip TDD and look at the screen after the changeDetectability moves to immediately and Reversibility to trivial, because a revert removes a wrong label
Nobody can state the correct output in advance, such as a search ranking tuned by feelTest it differently: write no test first; track clicks on the top results and alert when they dropTest cost rises to heavy, because a useful test needs queries with judged results, while Detectability stays eventually
The code is a rule that archives idle projects, which changes a few times a yearTest minimally: one test written first for the main path, and a failing test before each bug fixChange frequency falls to rarely, so fewer changes can break the rule

What breaks if you don't test

A test written after the code often takes its expected value from what the code returned. If the code has a bug, the test records the bug as correct, CI stays green, and customers find the wrong result weeks later. A test written first has failed at least once, so you know it can catch the missing behaviour.

What you lose if you over-test

TDD on every class tends to produce one test per method, with a mock for each collaborator. A refactor that moves a method between classes then breaks dozens of tests while behaviour stays the same. In a Microsoft Research study of three Microsoft teams and one IBM team, TDD projects took 15 to 35 percent longer to build, a price that layout tweaks and throwaway spikes do not repay.

How to test

  1. List the cases from the requirement before any code, as Martin Fowler describes.
  2. Pick one case, write a test for it, run it, and read the failure message.
  3. Write the least code that passes, then refactor while the tests stay green.
  4. Test rules through the public method of the service at the unit level, and queries with an integration test against a real database.
  5. Before each bug fix, write the test that reproduces the bug and watch it fail.

When the answer changes

  • The code moves money or decides who may see a record.
  • Nobody knows the right output until they see it, as with prototypes and visual design.
  • Your team requires test-first on every change: follow the team rule.

Real incident + Code example

The due date the test agreed with

On a helpdesk product I worked on, dueDate computed when a ticket escalated to a manager. The developer wrote the function, ran it, and pasted its output into the assertion. The function counted the opening day as a working day, so tickets escalated one day early. The test stayed green for three weeks, until a customer's support lead asked why managers got alerts for tickets still on time. We fixed the rule and ran a script over every open ticket. A test written first from the rule's text fails on the buggy code:

import { expect, it } from "vitest";
import { dueDate } from "./sla";

// Written after the code: the expected value came from its output
it("computes the due date", () => {
  expect(dueDate("2026-09-11", 2)).toBe("2026-09-14"); // Monday, the bug
});

// Written first, from the rule: the opening day does not count
it("gives a ticket opened on Friday with 2 working days until Tuesday", () => {
  expect(dueDate("2026-09-11", 2)).toBe("2026-09-15");
});

FAQ

Is TDD worth it?

TDD is worth it for rules and calculations whose correct result you can state before writing them. In a Microsoft Research study of four industrial teams, TDD teams had 60 to 90 percent lower defect density than comparable teams without TDD.

Should I start TDD on a project that has no tests?

Yes, start TDD on a project with no tests for each new rule and each bug fix, without backfilling tests for code nobody changes. Before changing old code, record what it returns today with characterization tests, then write the failing test for the new behaviour.

Does it matter whether I write the test first or last?

The order of test and code matters less than the size of the steps, according to Fucci and colleagues (2016): short, steady cycles of test and code were linked to better quality, and test order had no important effect. Writing the test first still keeps expected values from being copied out of the code's output.