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