Skip to main content

Assertions and Test Structure

intermediate18 min readLesson 100 of 143

The mechanics beneath every test framework: comparisons that fail loudly, before/after hooks, and organizing suites that scale.

Every test framework โ€” Jest, Vitest, Mocha โ€” is sugar over the same core: run code, compare, report. Build the core once and you'll understand all of them.

Assertions: fail loudly, precisely

function assertEqual(actual, expected, msg) {
  if (!Object.is(actual, expected)) {
    throw new Error(`${msg ?? "assert failed"}: expected ${fmt(expected)}, got ${fmt(actual)}`);
  }
}

Design rules learned the hard way:

  • The message names the expectation, not the mechanism. "Cart total excludes negative quantities" beats "0 !== -5".
  • Deep equality for objects: Object.is compares references; you need a structural compare (or JSON.stringify as a crude shortcut โ€” order-sensitive, undefined-hostile).
  • assertThrows(fn, ErrorType) must verify the type (and ideally the message) โ€” "it threw something" is too weak.

Structuring suites

describe("formatPrice", () => {
  it("formats whole dollars without cents", () => {
    /* ... */
  });
  it("always shows two decimals for fractional prices", () => {
    /* ... */
  });
});

describe groups one subject; it states one behavior. Nested describes encode context: describe("with an empty cart"). If a test name needs "and", split it.

Hooks

  • beforeEach โ€” fresh state per test (the default choice; independence!)
  • afterEach โ€” cleanup (restore stubbed globals, clear storage)
  • beforeAll โ€” expensive shared setup (start a server); use sparingly because it couples tests

The classic bug source: tests sharing mutable state (a module-level array one test appends to). Fresh state per test costs milliseconds and buys reliability.

Edge cases first

For any function, before the happy path, enumerate: empty input (array, string, object), boundaries (0, -1, max), wrong types (null, undefined, non-numbers), and duplicates. Most production bugs live there โ€” write the test that would have caught yours last time.

Now practice

Build Your Own Assertions โ€” PracticeConstruct the machinery beneath every framework: deep equality, typed throw-assertions, and diff-friendly messages.3 challenges ยท ยท ~18 minDesigning Test Cases โ€” PracticeThink like a test designer: enumerate edge cases for a spec, name behaviors precisely, and choose pyramid levels.3 challenges ยท ยท ~15 min