Course progress0%
Course content
60 min

Tests That Challenge the Contract

Test domain decisions and adapters independently of the DOM

By the end of this lesson

  • Test domain decisions and adapters independently of the DOM
  • Choose counterexamples that distinguish a correct implementation from a plausible one

A passing example is not enough

A test using only the premium 150.00 baseline cannot reveal whether the discount threshold is inclusive or exclusive. Your evidence should challenge a decision, not just execute a function.

Keep the Course 02 pricing tests. Add boundary cases for exactly 10000 cents, one cent above it, inactive customers and unsafe arithmetic. Test the new state transitions separately from price calculation: a valid price does not make a cancelled order editable.

Use the built-in runner

Create files ending in .test.js under tests/, import test from node:test and assert from node:assert/strict. Run node --test or your npm test script, whose command mapping was introduced in the local HTTP lab. The project has "type": "module", so ordinary .js files can use imports. Update the paths below to match your modules.

import test from 'node:test';
import assert from 'node:assert/strict';
import { parsePriceCents } from '../public/ui/form-values.js';

test('price parsing uses the agreed decimal format', () => {
  assert.equal(parsePriceCents('150.00'), 15000);
  assert.equal(parsePriceCents('0.5'), 50);
  assert.equal(parsePriceCents(' 12 '), 1200);
  for (const value of ['', '-1', '12abc', '1.234', '1,50', 'Infinity']) {
    assert.throws(() => parsePriceCents(value));
  }
});

The file path is a suggested location, not a file supplied by the platform. If you named your parser differently, adapt the import rather than creating a second parser just for the test.

Adapters need adversarial inputs

For storage, fake getItem/setItem with a Map. Test missing data, malformed JSON, wrong version, invalid draft and a thrown write. Assert that failed saving never clears the dirty flag.

For getProducts, inject a fake fetch. Return a successful Response containing invalid shape, then a 503 response. Assert rejection in both cases. Return { products: [] } with 200 and assert an empty array, not rejection. None of these tests should contact a public service.

For the confirmation store, test same-key replay, stale revision, key reuse with changed revision and validator rejection. Assert that a rejected transition leaves status and revision unchanged.

Mutate an assumption

Temporarily change the premium threshold from > to >=. Your exact-threshold test must fail. Temporarily remove the loader generation check: the overlap test must fail. Restore the implementation after each experiment.

If the suite still passes, it does not yet protect those decisions. Add the missing counterexample instead of increasing test count with more copies of the happy path.

Submit the suite and one documented mutation: the wrong change, the failing assertion and the business rule it protects.

Lesson complete?

Your progress is saved on this device.