mrkeyoor.com_
Sun 20 Sept 15:55 UTC
npmTestingupdated 20 Sept 2026

mocha review

Mocha 11.8.0 finds JavaScript test files, runs suites and hooks, applies timeouts and retries, and reports the outcome. Our Node 22 package check found a CommonJS runner with 21 direct dependencies, no bundled TypeScript declarations, and a browser entry that esbuild could not bundle. Mocha supplies BDD, TDD, exports, and QUnit-style interfaces along with serial or multi-process execution. Assertions, spies, module mocks, snapshots, transforms, and coverage are separate choices. Version 11.8.0 adds `--fail-hook-affected-tests`, which reports tests skipped after a hook failure as failed instead of leaving that consequence hidden in skipped counts.

Verdict

Our Mocha 11.8.0 install took 4.7 seconds, occupied 13 MB across 76 packages, and reported 3 vulnerabilities plus 1 deprecation warning, so its best case is an established suite or a deliberately modular test stack. Start a new Node service with node:test, or compare Vitest when transforms and integrated mocking are required.

We installed it

Lab card: what happened when we installed mochaScreenshot of mocha documentation
Install✓ · 4.7s76 packages on disk · 13 MB · 1 deprecation warning
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
Typesno TypeScript types found
Known vulns30 critical · 1 high · 1 moderate · 1 low (npm audit)

Answers from our run

Does mocha install cleanly?

Yes. In a fresh container with an empty cache, npm install mocha finished in 5 seconds, leaving 76 packages and 13 MB on disk. npm audit reported 3 known vulnerabilities. The install printed 1 deprecation warning.

Can mocha run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does mocha work with both ESM and CommonJS?

Yes. Both import 'mocha' and require('mocha') worked in Node 22 in our run. The package is published as CommonJS.

Does mocha include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

mocha or vitest: which should you use?

vitest: Choose it for Vite-aware TypeScript transforms with integrated mocks, snapshots, and coverage. Our Mocha 11.8.0 install took 4.7 seconds, occupied 13 MB across 76 packages, and reported 3 vulnerabilities plus 1 deprecation warning, so its best case is an established suite or a deliberately modular test stack.

When should you not use mocha?

Node's built-in test runner already covers ordinary Node-only suites, assertions, mocking, concurrency, and coverage without Mocha's 76-package install

API stability4/5Mocha 11.8.0 retains describe(), it(), hooks, callback or promise completion, timeouts, retries, and reporter selection used by long-running suites. Configuration changes carry more risk than ordinary specs: parallel mode has documented conflicts, and --fail-hook-affected-tests changes the reported outcome after hook failure. The CommonJS package also has no exports map, leaving programmatic consumers and custom reporters more exposed than CLI users.
Docs4/5The Mocha 11 site documents configuration selection, four interfaces, async completion rules, hooks, root-hook plugins, reporters, retries, timeouts, ESM, browser use, and parallel-mode incompatibilities. Its CLI pages name conflicting flags instead of leaving users to infer them. TypeScript still depends on external loader recipes and @types/mocha, while embedding the runner receives less task-oriented guidance than the command line.
Maintenance3/5Release 11.8.0 shipped on 2026-08-02, and GitHub records a repository push on 2026-08-25. The project is not archived and showed 265 open issues and pull requests. The release adds a CI-facing hook-failure option and updates Node 24 in publishing CI, which is current maintenance work. The README also states that volunteers maintain the project, so response time has no vendor support guarantee.
Ecosystem5/5npm recorded 15,039,656 downloads in the latest weekly window, and GitHub showed 22,905 stars. Chai, Sinon, c8, CI services, editors, and many reporter packages support Mocha's interfaces. That breadth protects established suites. Our clean install still reached 76 packages before adding assertions, spies, TypeScript loading, or coverage, which makes the ecosystem a maintenance choice as well as a benefit.

Use it if

  • Mocha 11 can keep an established suite running while the application changes around it
  • The team deliberately combines node:assert or Chai with Sinon and c8 instead of adopting one runner's complete testing stack
  • Integration tests need hooks, timeouts, retries, selectable reporters, and explicit process behavior more than source transforms
  • Root-hook plugins and .mocharc options must support several test packages with a shared runner policy
Skip it if

Setup reality

We installed Mocha 11.8.0 in 4.7 seconds in a fresh, unprivileged Node 22 Bookworm sandbox with 3 CPUs and 8 GB of RAM. npm printed 1 deprecation warning and left 76 packages occupying 13 MB. npm audit found 3 vulnerabilities: 1 high, 1 moderate, 1 low, and 0 critical. Mocha declares 21 direct dependencies, 0 peers, a 2,480 KB unpacked package, and Node ^18.18.0, ^20.9.0, or >=21.1.0.

The installed package is CommonJS with no exports map. require() and ESM import both succeeded, but no TypeScript declarations were present. Add @types/mocha plus a loader such as tsx for TypeScript discovery, and keep tsc --noEmit as its own check. Assertions can come from node:assert; spies and coverage still require other tools. Our esbuild browser build failed, so the installed entry is not a drop-in browser bundle.

Mocha takes CLI flags, one selected .mocharc source, or the mocha key in package.json. The default timeout is 2 seconds. Calls to this.timeout(), this.retries(), and this.skip() require function callbacks because arrow functions have no Mocha context. Databases, servers, and timers should close in teardown. --exit forces termination and can turn an open-handle defect into a passing 2-second shutdown.

Parallel mode starts test files in separate processes, so module state and file ordering cannot coordinate them. It rejects .only and conflicts with --file, --sort, and some reporters. Version 11.8.0's --fail-hook-affected-tests changes reporting after a failed hook by marking affected skipped tests as failures. Decide that CI policy explicitly; it can alter counts and failure output without executing one additional test.

Patterns

Run JavaScript specs under test install-and-run

npm install --save-dev mocha

// package.json
{
  "scripts": {
    "test": "mocha",
    "test:watch": "mocha --watch"
  }
}

# runs ./test/*.{js,cjs,mjs} by default
npm test

Mocha 11 checks JavaScript files directly under test by default; nested specs need --recursive or an explicit quoted glob.

Test a cart with node:assert write-a-test

import assert from 'node:assert/strict'
import { total } from '../src/cart.js'

describe('total', function () {
  it('sums line items', function () {
    assert.equal(total([{ price: 100, qty: 2 }]), 200)
  })

  it('rejects a negative quantity', function () {
    assert.throws(() => total([{ price: 100, qty: -1 }]), /quantity/)
  })
})

node:assert/strict covers equality and thrown errors without adding another package to the 76-package install.

Finish async tests with a promise or done async-tests

it('loads the user', async function () {
  const user = await getUser(1)
  assert.equal(user.id, 1)
})

it('emits done once', function (done) {
  emitter.once('ready', () => done())
  emitter.start()
})

A test that accepts done and also returns a promise fails because Mocha sees 2 completion signals.

Start and stop integration fixtures hooks-and-fixtures

describe('orders api', function () {
  let server, db

  before(async function () {
    db = await startDatabase()
    server = await startServer(db)
  })

  beforeEach(async function () {
    await db.truncate('orders')
  })

  after(async function () {
    await server.close()
    await db.close()
  })
})

A failed before hook skips its suite tests; after must close servers and databases so --exit is unnecessary.

Pin discovery and timeout in .mocharc config-file

// .mocharc.json
{
  "spec": ["test/**/*.spec.js"],
  "recursive": true,
  "timeout": 10000,
  "require": ["./test/setup.js"],
  "forbid-only": false,
  "reporter": "spec"
}

Mocha selects one config source, then CLI values override it; it does not merge every .mocharc format it finds.

Give a slow export 2 retries timeouts-and-retries

describe('report export', function () {
  this.timeout(30000)

  it('renders the quarterly PDF', function () {
    this.retries(2)
    return renderReport()
  })

  it('is fast', function () {
    this.timeout(500)
  })
})

this.timeout() and this.retries() require function callbacks; the 2 retries apply to the test body, not a broken setup hook.

Fail CI when .only is committed focus-and-skip

describe.only('billing', function () {
  it.skip('handles refunds', function () {})
  it('charges the card', function () {})
})

# in CI, make a stray .only fail the build
mocha --forbid-only

--forbid-only turns a committed focus marker into a failure; parallel mode rejects .only independently.

Share ESM database hooks esm-and-setup-file

// package.json
{ "type": "module" }

// test/setup.js
export const mochaHooks = {
  async beforeAll() {
    globalThis.db = await startDatabase()
  },
  async afterAll() {
    await globalThis.db.close()
  },
}

# .mocharc.json
{ "require": ["./test/setup.js"] }

A module loaded through require can export mochaHooks; root-hook plugins are the documented shared lifecycle mechanism for parallel runs.

Run .ts specs through tsx typescript-tests

npm install --save-dev mocha tsx @types/mocha

// .mocharc.json
{
  "extension": ["ts"],
  "spec": ["test/**/*.spec.ts"],
  "require": ["tsx"]
}

extension finds .ts files and tsx executes them; tsc --noEmit remains separate because the loader is not the type-checking gate.

Run 4 test-file workers parallel-mode

mocha --parallel --jobs 4

// test/setup.js, required in .mocharc.json
export const mochaHooks = {
  beforeEach() { /* runs in every worker */ },
}
export const mochaGlobalSetup = async () => { /* runs once, main process */ }

Four worker processes do not share module state or file order, and parallel mode conflicts with --file, --sort, and .only.

Rerun specs after source changes watch-mode

mocha --watch --watch-files 'src/**/*.js,test/**/*.js' --reporter dot

Watch mode keeps one process alive, so mutable module state can make a rerun differ from a clean mocha invocation.

Enforce 80 percent line coverage coverage-and-ci

npm install --save-dev c8

// package.json
{
  "scripts": {
    "test": "mocha",
    "test:ci": "c8 --check-coverage --lines 80 mocha --forbid-only --fail-zero --reporter spec"
  }
}

c8 supplies coverage; --fail-zero stops a missing spec glob from passing after 0 tests execute.

Alternatives

PackageRegistryPick it when
vitestnpmChoose it for Vite-aware TypeScript transforms with integrated mocks, snapshots, and coverage
jestnpmChoose it for built-in module mocking, snapshots, and established Babel or React Native support
avanpmChoose it for isolated processes, default concurrency, and its own assertion API
tapnpmChoose it for TAP output, assertions, isolation, and coverage in one toolchain

More testing guides

pytest · chai · vitest · jsdom · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.