---
url: https://huldoser.github.io/test-inventory/frameworks/mocha.md
description: >-
  Scan Mocha tests without running them, interpret the inventory, and check
  Mocha syntax and limitations.
---

# Mocha

This guide covers **Mocha 10.0 or newer**. You need Node.js 22.12 or newer to run test-inventory and an
existing JavaScript or TypeScript test suite. The scanner reads your source without executing your tests or starting
a browser. These are minimum supported versions; [CI comparisons](/guide/runners#checked-in-ci) list tested versions.

[Choose another framework](/frameworks/) · [Use an AI client](/guide/agents)

## Install

```sh
npm install --save-dev test-inventory
```

## Scan your suite

Run from the folder containing your project's `package.json`:

```sh
npx test-inventory --framework mocha --pretty --output inventory.json
```

To select files explicitly, use quoted globs. This example matches the `fix-gateway` fixture used below;
adjust the globs to your suite's layout:

```sh
npx test-inventory "test/**/*.spec.js" --framework mocha --output inventory.json
```

The command replaces `inventory.json` once the new output is complete. It does not change your test files.

## Check the result

Print the counts and diagnostics from the saved file:

```sh
node -e "const i = require('./inventory.json'); console.log(i.summary); console.log(i.diagnostics)"
```

For matching tests, `summary.fileCount` and `summary.testCount` should be greater than zero. Review diagnostics
even when the command exits with 0: errors can mean tests are missing. If the result is empty or unexpected,
use [troubleshooting](/guide/troubleshooting).

The inventory describes source declarations, not execution results. Conditional skips are not proof that a run
skipped the test. Loops and tables are one record by default; supported cases can be expanded with `--expand-cases`.
Expected-to-fail tests execute and check for failure; they are not disabled tests.

Tests created by unreadable helpers can be missing without a diagnostic. A dependency hash does not prove complete
coverage of runtime dependencies. Read the [shared limits](/guide/limits) before using scans to make CI decisions.

## Find skipped tests

```sh
npx test-inventory --framework mocha --state skip --fields fullName,state,condition,reason --pretty
```

This uses the same file selection as the first scan. If you supplied globs, `--root` or `--framework-version`,
repeat those options here.

This selects source declarations marked skip; it is not a report of a test run.
See [query recipes](/guide/recipes) for other inventory queries.

## Example

`fix-gateway` sends orders to venues over FIX and is tested with Mocha, in plain CommonJS. Its config is
`.mocharc.yml`:

```yaml excerpt
# Unit specs and the exchange specs, which share the session helpers in test/helpers.
spec: 'test/**/*.spec.js'
timeout: '5s'
retries: 1
```

The specs set timeouts and retries through Mocha's `this`, in describes and tests written as functions, and skip
themselves at run time:

```js excerpt
// test/session.spec.js
describe('FIX session', () => {
  // ...
  context('heartbeats', function () {
    this.retries(3);
    // ...
    it('answers a test request with a heartbeat', function () {
      this.timeout(250);
      // ...
  describe('logon to a live venue', function () {
    before(function () {
      if (!process.env.FIX_HOST) this.skip();
    });
```

```sh
npx test-inventory "test/**/*.spec.js" --framework mocha --output inventory.json
```

Without the glob and `--framework`, the scan finds the same files from `.mocharc.yml`, and Mocha from `package.json`.
The test's own timeout wins over the config's `'5s'`, and the retries come from its describe:

```json
{
  "fullName": "FIX session > heartbeats > answers a test request with a heartbeat",
  "timeout": 250,
  "timeoutSource": "test",
  "retries": 3,
  "retriesSource": "suite"
}
```

`this.skip()` in the `before` hook skips the describe's tests when the condition holds:

```json
{
  "fullName": "FIX session > logon to a live venue > logs on with a sequence reset",
  "state": "skip",
  "stateSource": "suite",
  "isConditional": true,
  "condition": "!process.env.FIX_HOST"
}
```

Other Mocha examples: [Puppeteer on Mocha](/frameworks/mocha-examples#puppeteer-on-mocha).

## Configuration and limits

The scan reads the first `.mocharc.cjs`, `.js`, `.mjs`, `.yaml`, `.yml`, `.jsonc` or `.json` in the root or an
ancestor, combined with the nearest `package.json` `mocha` key. List options such as `spec`, `extension` and
`ignore` are joined; other file values win. Without either config, Mocha's defaults apply.

Explicit scan globs count as supplied specs, less `ignore`, because project scripts often pass Mocha its files.
The config's `timeout`, `retries` and `forbid-only` are read. Runtime or package-provided settings remain unknown.
`this.skip()` is read from source; whether a conditional call actually skips a test needs a run.

::: details Exact file selection

For Mocha:
from the root, each `spec` entry as a glob, a file, a file named without its extension, or a folder, whose files with
one of the `extension`s, `js`, `cjs` and `mjs` by default, are taken, those in folders below too with `recursive`,
without those `ignore` matches as written, so that a pattern starting with `./` matches nothing; and the `file` entries,
which Mocha loads first, and to which `ignore` doesn't apply. An extension may have several parts, as `spec.js`. The
default `spec` is `./test`. Scanned with globs, every file they match counts as run, without those `ignore` matches, as
a project's scripts often give Mocha the spec on its command line, which no config shows. The config's `timeout`, which
may be a string such as `'5s'`, and `retries` apply to every test.

:::

## Supported syntax

Mocha 10.0 and newer.

### Where test comes from

| Form                       | Example                                                                                                                                                                       |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Globals                    | `describe`, `context`, `it`, `specify`, `xdescribe`, `xcontext`, `xit` and `xspecify` used without an import or declaration; with `ui: tdd` in the config, `suite` and `test` |
| Named import, also renamed | `import { describe, it } from 'mocha'`                                                                                                                                        |
| The module                 | `import * as mocha from 'mocha'`, a default import, or `require('mocha')`, whole, destructured or in place                                                                    |
| Kept in a variable         | `const onCi = process.env.CI ? it.skip : it`: only what both branches share applies, also to a choice written in place, as `(process.env.CI ? it.skip : it)(...)`             |
| Local helpers file         | `import { it } from './helpers'` or `require('./helpers')`, followed through local imports, and helper functions that declare tests, read at each call                        |

`xit`, `xspecify`, `xdescribe` and `xcontext` are `.skip`. Mocha's QUnit and exports interfaces, and a `ui` that names
a package, are reported on the config with a `config-value-unknown` warning, and the files are read as BDD.

### Calls

| Call                                                                               | Read as                                                                                                                                                                                                                                                           |
| ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `it(title, fn)`, `specify(title, fn)`                                              | A test.                                                                                                                                                                                                                                                           |
| `it(title)` without a function                                                     | A skipped test: Mocha reports it as pending.                                                                                                                                                                                                                      |
| `.only`, `.skip`                                                                   | `isOnly`, or a skipped test or describe. Any other member, as `it.concurrent`, is an `invalid-chain` error: Mocha stops before it runs any test, in this file or any other.                                                                                       |
| `describe(title, fn)`, `context(title, fn)`                                        | A describe. An async callback is allowed, as Mocha allows it.                                                                                                                                                                                                     |
| `describe(title)` without a function                                               | A `test-without-body` error: Mocha stops before it runs any test. `describe.skip(title)` is allowed.                                                                                                                                                              |
| `it()`, `describe()` without a title                                               | A `test-without-body` error: Mocha stops before it runs any test.                                                                                                                                                                                                 |
| `before`, `after`, `beforeEach`, `afterEach`, with or without a title              | Hooks, counted as `beforeAll`, `afterAll`, `beforeEach` and `afterEach`; with `ui: tdd`, `suiteSetup`, `suiteTeardown`, `setup` and `teardown`.                                                                                                                   |
| `this.timeout(ms)`, `this.retries(n)` in a describe written as a `function`        | `timeout` and `retries` on its tests and those of the describes inside it. A timeout may be a number or a string such as `'2s'`; `0`, and one of 2,147,483,647 ms or more, turns it off.                                                                          |
| `this.timeout(ms)`, `this.retries(n)` in a test written as a `function`            | The test's own `timeout` and `retries`. In a hook, `this.timeout()` sets the hook's own timeout, which no record holds.                                                                                                                                           |
| `it(...).timeout(ms)`, `it(...).retries(n)`, `describe(...).timeout(ms)`           | The same settings on the test the call returns. On a describe, only the timeout, and only from Mocha 12, which reads it as each test runs; before, a describe's tests take its timeout when they are added. `describe(...).retries(n)` reaches none of its tests. |
| `this.skip()` in a test or a `before` or `beforeEach` hook written as a `function` | The test, or the tests the hook runs for, skipped; under a condition, `isConditional`.                                                                                                                                                                            |

`this` is Mocha's context only directly in the test's, hook's or describe's own function: in an arrow function, or a
function inside it, `this.timeout()` and `this.skip()` are not read. A test takes its describe's `retries`, and before
Mocha 12 its `timeout`, when it is added, so a test written above `this.retries()` in the same describe doesn't get
them; the scan applies them to every test of the describe. A `beforeEach` or other hook written at the top level of one
spec file runs around the tests of every file, but is counted only in that file. A loop that declares tests, such as
`['XNAS', 'XLON'].forEach((mic) => it(...))`, is one record with `caseCount`, as for the other runners. Root hooks,
which a file the config lists in `require` exports as `mochaHooks`, run around every test, but are not counted in
`hooks`; the file is in each test's `dependencies`.

The [shared syntax rules](/reference/syntax#in-every-runner) also apply.

## Next

* [Shared limits](/guide/limits): imports, generated tests, conditions and dependency coverage.
* [Key concepts](/guide/concepts): counts, states, IDs and hashes.
* [Output reference](/reference/output) and [diagnostics](/reference/diagnostics): interpret every field and warning.
* [CLI](/guide/cli), [library](/guide/library) and [MCP](/guide/agents): use the same inventory through each interface.
* [Compare with Mocha](/guide/runners#mocha-s-list): differences from runner collection and execution reports.
