Mocha 훅
Mocha 훅 (Hooks) — 테스트 전후 준비와 정리
테스트를 제대로 쓰려면 각 테스트가 독립적으로 돌도록, 시작 전에 환경을 준비하고 끝나면 치워 주는 일이 반복돼요. Mocha는 이걸 before() · after() · beforeEach() · afterEach() 네 가지 훅으로 처리해요. 이 훅들은 BDD 인터페이스의 기본 기능이라 대부분의 테스트에서 자연스럽게 쓰게 됩니다.
네 가지 훅의 역할
before()— 그 블록의 첫 번째 테스트가 실행되기 전에 한 번만 실행돼요after()— 그 블록의 마지막 테스트가 끝난 뒤 한 번만 실행돼요beforeEach()— 그 블록 안의 각 테스트 전에 실행돼요afterEach()— 그 블록 안의 각 테스트 후에 실행돼요
describe("hooks", function () {
before(function () {
// runs once before the first test in this block
});
after(function () {
// runs once after the last test in this block
});
beforeEach(function () {
// runs before each test in this block
});
afterEach(function () {
// runs after each test in this block
});
// test cases
});
테스트는 훅의 앞에 놓아도, 뒤에 놓아도, 훅과 섞어 놓아도 상관없어요. 실행 순서는 정의된 순서를 따릅니다: 모든 before() 훅이 (한 번) 실행된 뒤, 각 테스트마다 beforeEach() → 테스트 → afterEach()가 돌고, 마지막에 after() 훅이 (한 번) 실행돼요.
훅에 설명 붙이기
어떤 훅이든 선택적으로 설명(description)을 붙일 수 있어요. 그러면 테스트가 실패했을 때 어느 훅이 문제인지 짚기 훨씬 쉬워져요. 이름 있는 함수를 넘기면 설명을 안 주어도 그 함수 이름이 대신 쓰여요.
beforeEach(function () {
// beforeEach hook
});
beforeEach(function namedFun() {
// beforeEach:namedFun
});
beforeEach("some description", function () {
// beforeEach:some description
});
비동기 훅
네 가지 훅은 모두 일반 테스트 케이스처럼 동기이거나 비동기일 수 있어요. 예를 들어 각 테스트 전에 DB에 더미 데이터를 채우고 싶다면, 아래처럼 done 콜백을 쓰는 비동기 훅으로 만들 수 있죠.
describe("Connection", function () {
const db = new Connection(),
tobi = new User("tobi"),
loki = new User("loki"),
jane = new User("jane");
beforeEach(function (done) {
db.clear(function (err) {
if (err) return done(err);
db.save([tobi, loki, jane], done);
});
});
describe("#find()", function () {
it("respond with matching records", function (done) {
db.find({ type: "User" }, function (err, res) {
if (err) return done(err);
res.should.have.length(3);
done();
});
});
});
});
루트 레벨 훅
스위트(suite) 밖, 테스트 파일의 최상위 스코프에 정의된 훅을 **루트 훅(root hook)**이라고 불러요. 특별한 설정이 필요할 때 쓸 수 있는데, v8.0.0부터는 Root Hook Plugins가 이 루트 훅을 잡는 더 권장되는 방식이에요.
지연된 루트 스위트 (Delayed Root Suite)
:::caution 지연된 루트 스위트는 병렬 모드(parallel mode)와 함께 쓸 수 없어요. :::
어떤 스위트도 실행되기 전에 (예: 테스트를 동적으로 생성하기 위해) 비동기 작업을 먼저 해야 한다면, 루트 스위트의 실행을 지연시킬 수 있어요. mocha를 --delay 플래그와 함께 실행하면 됩니다. 이렇게 하면 전역 컨텍스트에 run()이라는 특별한 콜백 함수가 붙어요.
--delay가 붙으면 Mocha는 테스트 파일을 불러와 최상위 코드를 평소처럼 실행하되, run()을 호출하기 전까지는 테스트 실행을 기다려요. 그래서 describe()·it() 호출을 비동기 코드 안에 두어 셋업이 끝난 뒤 스위트가 등록되게 하고, 그 다음 run()을 불러 실행을 시작하는 방식이에요.
import assert from "node:assert";
const fn = async (x) => {
return new Promise((resolve) => {
setTimeout(resolve, 3000, 2 * x);
});
};
// instead of an IIFE, you can use 'setImmediate' or 'nextTick' or 'setTimeout'
(async function () {
const z = await fn(3);
describe("my suite", function () {
it(`expected value ${z}`, function () {
assert.strictEqual(z, 6);
});
});
run();
})();