Why does a Mocha test suite pass locally but fail in CI when using the --exit flag?
0 reputation · 08 Jun 2021, 15:03 UTC
0 reputation · 08 Jun 2021, 15:03 UTC
Goal: Ensure that a Mocha test suite exits with the same status code and warning output when run locally on a developer machine and in a CI container.
Constraints: Locally the suite finishes quickly with no warnings, but in CI the same command sometimes fails due to exceeding the default 2000 ms test timeout, lingering asynchronous operations that keep the Node process alive when --exit is used, or because CI’s Node.js configuration treats unhandled promise rejections as failures. Additionally, the effective Mocha options may differ if a mocha.opts file or package.json mocha object is overridden by environment‑specific scripts or MOCHA_OPTS.
Specific questions: Does enabling the --exit flag guarantee immediate process termination in CI when pending async operations remain? Which configuration source (mocha.opts, package.json, CLI) takes precedence when CI scripts set MOCHA_OPTS? How can one verify that unhandled promise rejections are not being converted to failures by CI’s Node.js strict mode?
35275 reputation · 08 Jun 2021, 15:33 UTC
The --exit flag does not change test outcomes; it only forces process.exit() after the reporter finishes instead of waiting for the event loop to drain. If the suite passes locally but fails in CI with --exit, the failure is almost always a real test failure (timeout, unhandled rejection, or config difference) that --exit surfaces by terminating the process immediately rather than letting it hang.
--timeout 10000) or optimizing the slow operation.--unhandled-rejections=warn) may only warn. Pin the Node version in CI (e.g., .nvmrc or container tag) to match local.package.json mocha object or .mocharc.* apply when CLI doesn't specify. The legacy test/mocha.opts was removed in Mocha 8; if CI and local run different Mocha majors, the same mocha.opts is silently ignored in one environment. MOCHA_OPTS is not read by Mocha itself—it only takes effect if your CI script explicitly passes it (e.g., mocha $MOCHA_OPTS).Your CI failure is probably a timeout or unhandled rejection that --exit reveals by cutting off the process before you see the full stack trace locally. Without --exit, the local process would hang on the same lingering handles, making the problem invisible.
node --version
npx mocha --version--exit. If the process hangs, list open handles to find the leak:
node --trace-warnings ./node_modules/.bin/mochanode --unhandled-rejections=strict -e "Promise.reject(new Error('test'))"package.json scripts, and any wrapper for injected flags or MOCHA_OPTS usage.What exact Node and Mocha versions run in CI vs. locally? Version drift is the single most common hidden cause and changes whether you need a timeout bump, a rejection-handling fix, or a config alignment.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.