Mocha Setup Guide

describe / it / before / after

const assert = require('assert'); const { UserService } = require('../src/UserService'); describe('UserService', function () { let service; let db; before(async function () { // Runs once before all tests in this suite db = await connectTestDB(); }); after(async function () { // Runs once after all tests in this suite await db.close(); }); beforeEach(async function () { // Runs before each test service = new UserService(db); await db.seed([{ id: 1, name: 'Alice' }]); }); afterEach(async function () { // Runs after each test await db.clearAll(); }); it('finds user by id', async function () { const user = await service.findById(1); assert.strictEqual(user.name, 'Alice'); }); it('returns null for unknown id', async function () { const user = await service.findById(999); assert.strictEqual(user, null); }); describe('create()', function () { it('saves user to database', async function () { const created = await service.create({ name: 'Bob' }); assert.ok(created.id); }); }); });

.mocharc.yml / .mocharc.json

# .mocharc.yml spec: 'test/**/*.test.js' require: - '@babel/register' - 'test/helpers/setup.js' timeout: 5000 retries: 2 reporter: spec color: true recursive: true exit: true # force exit after tests complete # Parallel execution parallel: true jobs: 4 --- # .mocharc.json (alternative) { "spec": "test/**/*.spec.{js,mjs}", "timeout": 10000, "reporter": "mocha-junit-reporter", "reporterOptions": { "mochaFile": "test-results/junit.xml" }, "require": ["./test/setup.js"], "exit": true }

Reporters

# Built-in reporters mocha --reporter spec # Default: detailed mocha --reporter dot # Minimal dots mocha --reporter min # Summary only mocha --reporter tap # TAP protocol mocha --reporter json # JSON output mocha --reporter json-stream # Streaming JSON mocha --reporter nyan # Nyan cat 🐱 # Third-party reporters npm install mocha-junit-reporter mocha --reporter mocha-junit-reporter \ --reporter-options mochaFile=results/junit.xml npm install mochawesome mocha --reporter mochawesome \ --reporter-options reportDir=report,inline=true

Timeout & Retry

describe('API tests', function () { // Set timeout for entire suite this.timeout(10000); it('fetches data', async function () { // Override per-test this.timeout(3000); const data = await fetchData(); assert.ok(data); }); // Retry flaky tests it('flaky network test', async function () { this.retries(3); // retry up to 3 times const result = await unstableService.call(); assert.strictEqual(result.status, 'ok'); }); // Skip a test it.skip('not implemented yet', function () { // ... }); // Run only this test (exclusive) it.only('focus on this one', function () { assert.strictEqual(1 + 1, 2); }); });

Root Hooks & Global Fixtures

// test/hooks.js — root hook plugin exports.mochaHooks = { async beforeAll() { // Runs once before all test suites await startMockServer(); }, async afterAll() { await stopMockServer(); }, async beforeEach() { // Runs before each test across all files this.requestId = crypto.randomUUID(); }, }; // Register in .mocharc.yml // require: ['test/hooks.js'] // Global fixtures (Mocha v8+) // test/fixtures.js exports.mochaGlobalSetup = async function () { // Runs before all suites — no `this` context process.env.TEST_DB_URL = await startTestDatabase(); }; exports.mochaGlobalTeardown = async function () { await stopTestDatabase(); };

ESM Support

// .mocharc.yml for ESM spec: 'test/**/*.test.mjs' # No --require needed for native ESM // test/math.test.mjs import assert from 'node:assert/strict'; import { add, multiply } from '../src/math.mjs'; describe('math', () => { it('adds numbers', () => { assert.equal(add(2, 3), 5); }); }); // Mixed CJS/ESM: use .mjs extension or "type": "module" in package.json // package.json scripts { "scripts": { "test": "mocha", "test:watch": "mocha --watch", "test:coverage": "c8 mocha" } }