On this page

🌐 Mocha to Node.js Test Runner

Mocha 8.x 测试套件迁移到内置的 Node.js 测试运行器 (node:test,在 Node.js 22.x 和 24.x 中可用)。它会为文件使用的全局变量添加所需的 node:test 导入 (describe, it, before, after, beforeEach, afterEach),将 done 回调转换为 (t, done) 签名,把 this.skip() 重写为 t.skip()this.timeout(N) 重写为 { timeout: N } 选项,并保留原有函数风格(它不会在 function() 和箭头函数之间转换)。同时支持 CommonJS 和 ESM 文件,并且之后会从 package.json 中移除 mocha@types/mocha 依赖。

🌐 Migrates Mocha 8.x test suites to the built-in Node.js test runner (node:test, available in Node.js 22.x and 24.x). It adds the required node:test imports for the globals a file uses (describe, it, before, after, beforeEach, afterEach), converts done callbacks to the (t, done) signature, rewrites this.skip() to t.skip() and this.timeout(N) to { timeout: N } options, and preserves the original function style (it never converts between function() and arrow functions). Both CommonJS and ESM files are supported, and the mocha and @types/mocha dependencies are removed from package.json afterwards.

🌐 Usage

使用这个 codemod 运行:

🌐 Run this codemod with:

🌐 Examples

🌐 Adding node:test imports (CommonJS)

一旦插入匹配的 require('node:test'),全局 describe/it 的使用就会继续有效;像 describe.skip 这样的修饰符已经兼容。

🌐 Global describe/it usage keeps working once the matching require('node:test') is inserted; modifiers like describe.skip are already compatible.

 const assert = require('assert');
+const { describe, it } = require('node:test');

 describe('Array', function() {
   describe.skip('#indexOf()', function() {
     it('should return -1 when the value is not present', function() {
       const arr = [1, 2, 3];
       assert.strictEqual(arr.indexOf(4), -1);
     });
   });
 });

🌐 Adding node:test imports (ESM)

在 ESM 文件中,改为插入一个 import 语句。

🌐 In ESM files an import statement is inserted instead.

 import assert from 'assert';
+import { describe, it } from 'node:test';

 describe('Array', function() {
   describe.skip('#indexOf()', function() {
     it('should return -1 when the value is not present', function() {

🌐 Hooks

只有文件中实际使用的钩子会被添加到导入列表里。

🌐 Only the hooks actually used in the file are added to the import list.

 const assert = require('assert');
 const fs = require('fs');
+const { describe, before, after, it } = require('node:test');

 describe('File System', () => {
   before(function() {
     fs.writeFileSync('test.txt', 'Hello, World!');
   });

   after(() => {
     fs.unlinkSync('test.txt');
   });

🌐 done callbacks

Mocha 把 done 作为第一个回调参数传递;node:test 则先传递测试上下文,所以 (done) 就变成了 (t, done)

🌐 Mocha passes done as the first callback argument; node:test passes the test context first, so (done) becomes (t, done).

 const assert = require('assert');
+const { describe, it } = require('node:test');

 describe('Callback Test', function() {
-  it('should call done when complete', function(done) {
+  it('should call done when complete', function(t, done) {
     setTimeout(() => {
       assert.strictEqual(1 + 1, 2);
       done();
     }, 100);
   });
 });

🌐 Skipping with this.skip()

this.skip() 变成 t.skip(),根据需要在回调签名中添加测试上下文参数 t

 const assert = require('assert');
+const { describe, it } = require('node:test');

 describe('Skipped Test', () => {
   it.skip('should not run this test', () => {
     assert.strictEqual(1 + 1, 3);
   });
-  it('should also be skipped', () => {
-    this.skip();
+  it('should also be skipped', (t) => {
+    t.skip();
     assert.strictEqual(1 + 1, 3);
   });

-  it('should also be skipped 2', (done) => {
-    this.skip();
+  it('should also be skipped 2', (t, done) => {
+    t.skip();
     assert.strictEqual(1 + 1, 3);
   });
 });

🌐 Timeouts

this.timeout(N) 调用套件和测试会移到 { timeout: N } 的选项参数里。

 const assert = require('assert');
+const { describe, it } = require('node:test');

-describe('Timeout Test', function() {
-  this.timeout(500);
+describe('Timeout Test', { timeout: 500 }, function() {

-  it('should complete within 100ms', (done) => {
-    this.timeout(100);
+  it('should complete within 100ms', { timeout: 100 }, (t, done) => {
     setTimeout(done, 500); // This will fail
   });

-  it('should complete within 200ms', function(done) {
-    this.timeout(200);
+  it('should complete within 200ms', { timeout: 200 }, function(t, done) {
     setTimeout(done, 100); // This will pass
   });
 });

🌐 Notes

  • 转换完成后,codemod 会检测你的包管理器,并从 package.json 中移除 mocha@types/mocha 依赖。

🌐 Limitations

  • node:test 不支持 Mocha 的 retry 选项,所以依赖它的测试需要单独处理。