BugForge
All lessons
Node.js · 12/19 6 min

Reading Jest failures fast

toBe vs toEqual, toThrow needs a function, async tests, supertest and fake timers.

The failing test is the spec. Read its Expected / Received block before you open any source file.

Matchers that confuse people

Matcher Checks Trap
toBe Object.is identity two equal objects are not toBe each other
toEqual deep equality ignores undefined properties
toStrictEqual deep + undefined + class stricter than you think
toMatchObject the listed subset extra fields are fine
toBeCloseTo floats use for money calculated as floats
toHaveProperty("a.b") path exists not.toHaveProperty("password") is how leaks are caught

Throwing

expect(parse("x")).toThrow();          // ❌ parse throws BEFORE expect runs
expect(() => parse("x")).toThrow("invalid");   // ✅ pass a function
await expect(save(bad)).rejects.toThrow("invalid");   // async version

Async tests

test("creates", async () => {
  const res = await request(app).post("/api/items").send({ name: "a" });
  expect(res.status).toBe(201);
  expect(res.body).toMatchObject({ name: "a" });
});

A test that forgets await (or return) can pass before the request even finishes. If a test passes suspiciously fast, check that.

Reading a supertest failure

expected 201, got 500 means the handler threw. Look at the logs for the stack; the bug is in the code path, not the status line. got 404 often means the route isn't mounted or the path/prefix is wrong.

Mocks and timers

const send = jest.fn();
notify(user, send);
expect(send).toHaveBeenCalledWith(user.email, expect.any(String));

jest.useFakeTimers();
retry(fn);
jest.advanceTimersByTime(1000);   // drive setTimeout without waiting

Rules for debugging rounds

  • Never edit the tests to make them pass.
  • Fix the first failing test first; later ones often share the cause.
  • After each fix, re-run the whole suite.
Practise it now
Next in Node.js
EventEmitter and streams