Skip to main content

One Test, Many Cases

Most functions need the same check for many inputs: normal values, negative values, zero, edge cases. Copying the test five times makes five places to maintain.

The usual answer is a table-driven test: put the cases into an array of objects — each case holds a name, the inputs and the expected output — and run one loop over them.

Adding a new case becomes one new line in the table. This is the most common test pattern you will see in real code.


The Table

The table is an array of objects with a named type, written right next to the test. Each entry is one case: a name (for the failure message), the inputs, and the expected result.

The loop calls the function once per case and compares the result. The first mismatch throws and ends the test, so the message carries the case name — that is how you know which row broke.

Four cases in one table

Four cases, one loop, one check. All pass here, so the run prints a single ✔ add over the table line. To test a fifth situation you would add one line to the table — the loop stays untouched.

Prompt
Write a table-driven TypeScript test for an add function: a typed array of objects with a case name, two number inputs and the expected result, covering one plus one, positive and negative, both negative, and zeros. Loop over the table inside one test and put the case name in the assertion message. Show the output.
Output: TypeScript file and captured output
Four cases in one table

Subtests

Calling t.test(name, fn) inside a test turns each table case into its own subtest. The outer test function receives the test context t as its argument, and each case is awaited so it finishes before the next one starts.

The case names now show up in the output, indented under the parent test, each with its own ✔ or ✖. That makes a broken case easy to spot in a long table.

Every case gets a name

The same table, wrapped in t.test. The output now lists all four cases separately under ▶ add, and the summary counts five tests: the parent plus its four subtests.

Prompt
Write the same table-driven TypeScript test for add, but run each case as an awaited subtest with t.test using the case name, so every case appears separately in the output. Show the output with all subtest lines.
Output: TypeScript file and captured output

Run One Case Alone

Tests can be picked from the command line by name with --test-name-pattern:

node --test --test-reporter=spec --test-name-pattern 'both negative'

The pattern is matched against the name of each registered test. A subtest cannot be selected on its own — when the parent test does not match, nothing inside it runs — so for this to work each case gets its own top-level test(…) call with the case name in it.

This is perfect while fixing one broken case: rerun just that case until it is green, then run everything again.

Filter by name

The table is registered as four separate tests, named add: one plus one and so on. Run with the pattern both negative, only one of the four executes — the summary says tests 1.

Prompt
Register the same add table as four separate top-level tests named after the cases, then show the output of running only one of them with the test runner's --test-name-pattern 'both negative' option.
Output: TypeScript file and captured output