"예전에는 Node project를 시작하면 Jest나 Mocha부터 넣었어. 지금은 runtime 안에 test runner가 있으니, 외부 도구가 필요한 이유부터 확인할 차례야."
설치 없이 바로 시작해
Node 20 이상에는 node:test가 들어 있어. node --test를 실행하면 정해진 이름의 test file을 찾아 돌려 주는 완전한 runner야. Package를 설치하거나 별도 설정 file을 만들지 않아도 되고, Node가 직접 이해하는 code라면 test만을 위한 transpile 단계도 필요 없어.
// test/add.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { add } from '../src/math.mjs';
test('add returns the sum of two numbers', () => {
assert.equal(add(1, 2), 3);
});
test('add handles negatives', () => {
assert.equal(add(-1, 1), 0);
});
기본 출력은 TAP 형식이고, 사람이 읽기 편한 spec 형식을 원하면 --test-reporter=spec을 붙이면 돼.
작은 runner라고 기능까지 작은 건 아니야
describe(name, fn)으로 관련 test를 묶을 수 있어.before,after,beforeEach,afterEachhook을 제공해.test.skip은 실행을 건너뛰고,test.todo는 아직 구현하지 않은 test를 표시해.test.only와--test-only를 함께 쓰면 표시한 test에 집중할 수 있어.- Test 안에서
t.test(...)를 불러 하위 test를 만들 수 있어. - Async function을 그대로 test body로 쓰거나 context
t를 받는 callback을 쓸 수 있어.
Mocha를 써 봤다면 낯설지 않은 모양이야. Coverage는 node --test --experimental-test-coverage로 수집할 수 있고, 새 Node에서는 안정화된 동등 기능을 확인해 쓰면 돼.
일반적인 mock도 내장돼 있어
node:test는 test context의 t.mock을 통해 method와 timer를 대신할 수 있어.
import { test } from 'node:test';
import assert from 'node:assert/strict';
import * as db from '../src/db.mjs';
test('fetches user from db', (t) => {
const stub = t.mock.method(db, 'findUser', () => ({
id: 1, name: 'Pippa'
}));
const result = getUser(1);
assert.equal(result.name, 'Pippa');
assert.equal(stub.mock.calls.length, 1);
});t.mock.timers.enable()로 시간도 제어할 수 있어. Jest보다 API 범위는 작지만 service test의 흔한 stub과 timer 제어에는 충분해. Snapshot이나 복잡한 fixture가 핵심인 project라면 Jest를 유지하고, 그렇지 않다면 내장 runner부터 검토해.
변경할 때마다 다시 돌리기
node --test --watch는 관련 file이 바뀌면 test를 다시 실행해. Server용 node --watch와 같은 runtime primitive라 test 재실행만을 위해 nodemon을 하나 더 둘 필요가 없어.
Jest를 떠날 때 잃는 기능도 계산해
- 전용 snapshot test API
expect(x).toEqual({...})같은 풍부한 matcher와 부분 deep matchingjest.mock('./module')처럼 import 자체를 다시 쓰는 자동 module mocking- Watch mode에서 원하는 test를 고르는 interactive UI
Project가 이 기능을 실제로 쓴다면 Jest가 맞아. 반대로 test 대부분이 assertion과 method mock 몇 개라면 node:test로 옮겨 devDependencies에서 큰 tree를 덜어 낼 수 있어. Dependency 크기보다 중요한 건 test code가 어느 runner의 고유 기능에 기대고 있는지 먼저 알아내는 거야.
Pippa의 고백
node:test에서 시작하고, Jest 계열 기능이 실제로 필요할 때만 더 큰 runner를 골라.