Handbooks / Node.js / Chapter 9
Testing Node Apps
7 pages · ~5 min✓ Reviewed
Builds on Authentication and Security Basics. Next up: Logging and Debugging.
Testing Node.js: Mental Model
1. Runner and assertions
describe/it/testregister tests; the runner runs them- A thrown error marks the test failed
- Runner: finds, runs, reports.
assert/expect: only throw on mismatch
2. Arrange-Act-Assert
- Set up inputs, call the unit once, assert the outcome
- One behavior per test
javascript
import { test } from 'node:test'; import assert from 'node:assert/strict'; test('adds tax', () => { const price = 100; // arrange const total = addTax(price); // act assert.equal(total, 120); // assert });
3. Unit vs integration
| Unit | Integration | |
|---|---|---|
| Scope | One module | Real pieces wired |
| Deps | Test doubles | Real or in-memory DB |
| Example | Service + fake repo | HTTP layer + DB |
Test behavior
Assert public output, not private calls. Internal assertions break on every refactor.
node:test and assert
1. Built In, No Install
import { test, describe, it, before, after, beforeEach, afterEach } from 'node:test'- Run with node --test: finds
*.test.jsand files undertest/ - Async test functions are awaited automatically
| Flag | Does |
|---|
Jest vs Vitest
1. Same API, different engine
- Both ship describe/it/expect, matchers, mocks, snapshots, coverage
- Vitest: Vite-powered, native ESM + TypeScript, fast watch
- Jest: needs a transform (babel or ts-jest) for ESM/TS
- Jest globals are automatic:
jest.fn,jest.mock - Vitest needs
globals: truein config, or an import - Vitest mock namespace is vi, not
jest
Mocking Modules and Timers
1. Doubles
| Double | Job |
|---|---|
| Spy | observes a real call |
| Stub | replaces the return value |
| Mock | adds call expectations |
HTTP Handlers, Database Doubles and Coverage
1. Testing Handlers with supertest
- Export
appwithout listen(); supertest binds an ephemeral port itself - Assert status, headers, then check
res.body - Test the 400, 401 and 404 paths, not only the happy path
javascript
const res = await request(app) .post('/users').send({ name: 'a' }) .set('Authorization', 'Bearer t') .expect(201) .expect('Content-Type', /json/); assert.equal(res.body.name, 'a');
Testing Cheat Sheet
1. Run and Assert
| Runner | Run | Filter by name |
|---|---|---|
| node:test | node --test | --test-name-pattern |
| Jest | npx jest | -t |
| Vitest |
Check yourself
This test stays green even when getUser(0) resolves instead of rejecting. Why?
assert.rejectsreturns a promise, and nothing awaits it.- The test ends first, so the check can't fail it.
- Fix: make the test
asyncandawaitthe call.
test('bad id', () => { assert.rejects(getUser(0), TypeError); });
Does expect({a:1}).toBe({a:1}) pass?
- It fails:
toBeusesObject.is, and these are two different objects.