測試執行器#

穩定性:2 - 穩定

原始碼: lib/test.js

node:test 模組可簡化 JavaScript 測試的建立。如要存取它

import test from 'node:test';const test = require('node:test');

此模組僅在 node: 架構下可用。以下程式碼無法執行

import test from 'test';const test = require('test');

透過 test 模組建立的測試包含一個單一函式,該函式會以三種方式之一進行處理

  1. 同步函式,如果擲回例外狀況,則視為失敗,否則視為通過。
  2. 傳回 Promise 的函式,如果 Promise 拒絕,則視為失敗,如果 Promise 完成,則視為通過。
  3. 接收回呼函式的函式。如果回呼函式將任何真值作為其第一個引數接收,則測試視為失敗。如果假值作為第一個引數傳遞給回呼函式,則測試視為通過。如果測試函式接收回呼函式,且也傳回 Promise,則測試會失敗。

以下範例說明如何使用 test 模組撰寫測試。

test('synchronous passing test', (t) => {
  // This test passes because it does not throw an exception.
  assert.strictEqual(1, 1);
});

test('synchronous failing test', (t) => {
  // This test fails because it throws an exception.
  assert.strictEqual(1, 2);
});

test('asynchronous passing test', async (t) => {
  // This test passes because the Promise returned by the async
  // function is settled and not rejected.
  assert.strictEqual(1, 1);
});

test('asynchronous failing test', async (t) => {
  // This test fails because the Promise returned by the async
  // function is rejected.
  assert.strictEqual(1, 2);
});

test('failing test using Promises', (t) => {
  // Promises can be used directly as well.
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      reject(new Error('this will cause the test to fail'));
    });
  });
});

test('callback passing test', (t, done) => {
  // done() is the callback function. When the setImmediate() runs, it invokes
  // done() with no arguments.
  setImmediate(done);
});

test('callback failing test', (t, done) => {
  // When the setImmediate() runs, done() is invoked with an Error object and
  // the test fails.
  setImmediate(() => {
    done(new Error('callback failure'));
  });
}); 

如果任何測試失敗,處理序結束代碼會設定為 1

子測試#

測試內容的 test() 方法允許建立子測試。它允許您以階層方式建構測試,您可以在較大的測試中建立巢狀測試。此方法的行為與頂層 test() 函數相同。以下範例示範如何建立具有兩個子測試的頂層測試。

test('top level test', async (t) => {
  await t.test('subtest 1', (t) => {
    assert.strictEqual(1, 1);
  });

  await t.test('subtest 2', (t) => {
    assert.strictEqual(2, 2);
  });
}); 

注意:beforeEachafterEach 勾子會在每個子測試執行之間觸發。

在此範例中,await 用於確保兩個子測試都已完成。這是必要的,因為父測試不會等待其子測試完成,這與使用 describeit 語法建立的測試不同。當父測試完成時,任何仍未完成的子測試都會被取消並視為失敗。任何子測試失敗都會導致父測試失敗。

略過測試#

個別測試可以透過傳遞 skip 選項給測試,或呼叫測試內容的 skip() 方法來略過,如下面的範例所示。

// The skip option is used, but no message is provided.
test('skip option', { skip: true }, (t) => {
  // This code is never executed.
});

// The skip option is used, and a message is provided.
test('skip option with message', { skip: 'this is skipped' }, (t) => {
  // This code is never executed.
});

test('skip() method', (t) => {
  // Make sure to return here as well if the test contains additional logic.
  t.skip();
});

test('skip() method with message', (t) => {
  // Make sure to return here as well if the test contains additional logic.
  t.skip('this is skipped');
}); 

describe/it 語法#

執行測試也可以使用 describe 來宣告套件,並使用 it 來宣告測試。套件用於組織和將相關測試分組在一起。ittest() 的簡寫。

describe('A thing', () => {
  it('should work', () => {
    assert.strictEqual(1, 1);
  });

  it('should be ok', () => {
    assert.strictEqual(2, 2);
  });

  describe('a nested thing', () => {
    it('should work', () => {
      assert.strictEqual(3, 3);
    });
  });
}); 

describeitnode:test 模組匯入。

import { describe, it } from 'node:test';const { describe, it } = require('node:test');

僅測試#

如果 Node.js 以 --test-only 命令列選項啟動,則可以略過所有頂層測試,只執行傳遞 only 選項給應執行的測試的選定子集。當執行設定 only 選項的測試時,也會執行所有子測試。測試內容的 runOnly() 方法可用於在子測試層級實作相同的行為。

// Assume Node.js is run with the --test-only command-line option.
// The 'only' option is set, so this test is run.
test('this test is run', { only: true }, async (t) => {
  // Within this test, all subtests are run by default.
  await t.test('running subtest');

  // The test context can be updated to run subtests with the 'only' option.
  t.runOnly(true);
  await t.test('this subtest is now skipped');
  await t.test('this subtest is run', { only: true });

  // Switch the context back to execute all tests.
  t.runOnly(false);
  await t.test('this subtest is now run');

  // Explicitly do not run these tests.
  await t.test('skipped subtest 3', { only: false });
  await t.test('skipped subtest 4', { skip: true });
});

// The 'only' option is not set, so this test is skipped.
test('this test is not run', () => {
  // This code is not run.
  throw new Error('fail');
}); 

依名稱篩選測試#

可以使用 --test-name-pattern 命令列選項,只執行名稱符合所提供模式的測試。測試名稱模式會解譯為 JavaScript 正規表示式。可以多次指定 --test-name-pattern 選項,以執行巢狀測試。對於執行的每個測試,也會執行任何對應的測試掛勾,例如 beforeEach()

假設有下列測試檔案,以 --test-name-pattern="test [1-3]" 選項啟動 Node.js,會導致測試執行器執行 test 1test 2test 3。如果 test 1 不符合測試名稱模式,則其子測試不會執行,儘管符合模式。也可以透過多次傳遞 --test-name-pattern 來執行相同的測試集(例如 --test-name-pattern="test 1"--test-name-pattern="test 2" 等)。

test('test 1', async (t) => {
  await t.test('test 2');
  await t.test('test 3');
});

test('Test 4', async (t) => {
  await t.test('Test 5');
  await t.test('test 6');
}); 

也可以使用正規表示式字面值來指定測試名稱模式。這允許使用正規表示式旗標。在先前的範例中,以 --test-name-pattern="/test [4-5]/i" 啟動 Node.js 會符合 Test 4Test 5,因為模式不分大小寫。

測試名稱模式不會變更測試執行器執行的檔案集。

外部非同步活動#

測試函數執行完畢後,結果會盡快回報,同時維持測試順序。然而,測試函數可能會產生非同步活動,而這些活動會比測試本身更長命。測試執行器會處理這類活動,但不會延遲回報測試結果以配合這些活動。

在以下範例中,一個測試完成,但仍有兩個 setImmediate() 作業尚未完成。第一個 setImmediate() 嘗試建立一個新的子測試。由於父測試已完成並輸出結果,新的子測試會立即標記為失敗,並稍後回報給 <TestsStream>

第二個 setImmediate() 建立一個 uncaughtException 事件。來自已完成測試的 uncaughtExceptionunhandledRejection 事件會由 test 模組標記為失敗,並由 <TestsStream> 在頂層回報為診斷警告。

test('a test that creates asynchronous activity', (t) => {
  setImmediate(() => {
    t.test('subtest that is created too late', (t) => {
      throw new Error('error1');
    });
  });

  setImmediate(() => {
    throw new Error('error2');
  });

  // The test finishes after this line.
}); 

監控模式#

穩定性:1 - 實驗性

Node.js 測試執行器支援透過傳遞 --watch 旗標來執行監控模式

node --test --watch 

在監控模式中,測試執行器會監控測試檔案及其相依項目的變更。當偵測到變更時,測試執行器會重新執行受到變更影響的測試。測試執行器會持續執行,直到程序終止。

從命令列執行測試#

Node.js 測試執行器可透過傳遞 --test 旗標從命令列呼叫

node --test 

預設情況下,Node.js 會執行符合下列模式的所有檔案

  • **/*.test.?(c|m)js
  • **/*-test.?(c|m)js
  • **/*_test.?(c|m)js
  • **/test-*.?(c|m)js
  • **/test.?(c|m)js
  • **/test/**/*.?(c|m)js

或者,也可以將一個或多個 glob 模式提供為 Node.js 命令的最後一個引數,如下所示。Glob 模式遵循 glob(7) 的行為。

node --test **/*.test.js **/*.spec.js 

符合的檔案會作為測試檔案執行。可以在 測試執行器執行模式 區段找到有關測試檔案執行的更多資訊。

測試執行器執行模式#

每個符合的測試檔案會在個別的子處理序中執行。在任何時間執行的子處理序最大數量由 --test-concurrency 旗標控制。如果子處理序以 0 的結束代碼結束,則測試會被視為通過。否則,測試會被視為失敗。測試檔案必須可由 Node.js 執行,但不需要在內部使用 node:test 模組。

每個測試檔案會像常規腳本一樣執行。也就是說,如果測試檔案本身使用 node:test 來定義測試,則所有這些測試都將在單一應用程式執行緒中執行,與 test()concurrency 選項值無關。

收集程式碼覆蓋率#

穩定性:1 - 實驗性

當 Node.js 以 --experimental-test-coverage 命令列旗標啟動時,程式碼覆蓋率會被收集,並且在所有測試完成後會回報統計資料。如果 NODE_V8_COVERAGE 環境變數用於指定程式碼覆蓋率目錄,則產生的 V8 覆蓋率檔案會寫入該目錄。Node.js 核心模組和 node_modules/ 目錄中的檔案不會包含在覆蓋率報告中。如果啟用覆蓋率,則覆蓋率報告會透過 'test:coverage' 事件傳送給任何 測試回報器

可以使用下列註解語法在多行中停用涵蓋範圍

/* node:coverage disable */
if (anAlwaysFalseCondition) {
  // Code in this branch will never be executed, but the lines are ignored for
  // coverage purposes. All lines following the 'disable' comment are ignored
  // until a corresponding 'enable' comment is encountered.
  console.log('this is never executed');
}
/* node:coverage enable */ 

也可以停用指定行數的涵蓋範圍。在指定行數之後,涵蓋範圍會自動重新啟用。如果未明確提供行數,則會略過單行。

/* node:coverage ignore next */
if (anAlwaysFalseCondition) { console.log('this is never executed'); }

/* node:coverage ignore next 3 */
if (anAlwaysFalseCondition) {
  console.log('this is never executed');
} 

涵蓋範圍報告器#

tap 和 spec 報告器會列印涵蓋範圍統計資料摘要。還有一個 lcov 報告器,會產生一個 lcov 檔案,可用作深入的涵蓋範圍報告。

node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info 

限制#

測試執行器的程式碼涵蓋範圍功能有下列限制,這些限制會在未來的 Node.js 版本中解決

  • 不支援原始碼對應。
  • 不支援從涵蓋範圍報告中排除特定檔案或目錄。

模擬#

node:test 模組支援在測試期間透過頂層 mock 物件進行模擬。下列範例建立一個間諜函式,將兩個數字加總。然後使用間諜函式來斷言函式呼叫符合預期。

import assert from 'node:assert';
import { mock, test } from 'node:test';

test('spies on a function', () => {
  const sum = mock.fn((a, b) => {
    return a + b;
  });

  assert.strictEqual(sum.mock.calls.length, 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.calls.length, 1);

  const call = sum.mock.calls[0];
  assert.deepStrictEqual(call.arguments, [3, 4]);
  assert.strictEqual(call.result, 7);
  assert.strictEqual(call.error, undefined);

  // Reset the globally tracked mocks.
  mock.reset();
});'use strict';
const assert = require('node:assert');
const { mock, test } = require('node:test');

test('spies on a function', () => {
  const sum = mock.fn((a, b) => {
    return a + b;
  });

  assert.strictEqual(sum.mock.calls.length, 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.calls.length, 1);

  const call = sum.mock.calls[0];
  assert.deepStrictEqual(call.arguments, [3, 4]);
  assert.strictEqual(call.result, 7);
  assert.strictEqual(call.error, undefined);

  // Reset the globally tracked mocks.
  mock.reset();
});

相同的模擬功能也公開在每個測試的 TestContext 物件上。下列範例使用 TestContext 公開的 API 建立一個間諜函式,用於物件方法。透過測試內容進行模擬的好處是,測試執行器會在測試完成後自動還原所有模擬功能。

test('spies on an object method', (t) => {
  const number = {
    value: 5,
    add(a) {
      return this.value + a;
    },
  };

  t.mock.method(number, 'add');
  assert.strictEqual(number.add.mock.calls.length, 0);
  assert.strictEqual(number.add(3), 8);
  assert.strictEqual(number.add.mock.calls.length, 1);

  const call = number.add.mock.calls[0];

  assert.deepStrictEqual(call.arguments, [3]);
  assert.strictEqual(call.result, 8);
  assert.strictEqual(call.target, undefined);
  assert.strictEqual(call.this, number);
}); 

計時器#

模擬計時器是軟體測試中常用的技術,用於模擬和控制計時器的行為,例如 setIntervalsetTimeout,而無需實際等待指定的時間間隔。

請參閱 MockTimers 類別以取得方法和功能的完整清單。

這允許開發人員為依賴時間的功能撰寫更可靠且可預測的測試。

以下範例顯示如何模擬 setTimeout。使用 .enable({ apis: ['setTimeout'] }); 它將模擬 node:timersnode:timers/promises 模組中的 setTimeout 函式,以及來自 Node.js 全域環境的 setTimeout 函式。

注意:此 API 目前不支援解構函式,例如 import { setTimeout } from 'node:timers'

import assert from 'node:assert';
import { mock, test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', () => {
  const fn = mock.fn();

  // Optionally choose what to mock
  mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);

  // Reset the globally tracked mocks.
  mock.timers.reset();

  // If you call reset mock instance, it will also reset timers instance
  mock.reset();
}); 
const assert = require('node:assert');
const { mock, test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', () => {
  const fn = mock.fn();

  // Optionally choose what to mock
  mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);

  // Reset the globally tracked mocks.
  mock.timers.reset();

  // If you call reset mock instance, it'll also reset timers instance
  mock.reset();
}); 

相同的模擬功能也顯示在每個測試的 TestContext 物件的 mock 屬性中。透過測試環境進行模擬的好處是,一旦測試完成,測試執行器將自動還原所有模擬計時器的功能。

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);
}); 
const assert = require('node:assert');
const { test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);
}); 

日期#

模擬計時器 API 也允許模擬 Date 物件。這是一個有用的功能,可用於測試依賴時間的功能,或模擬內部日曆函式,例如 Date.now()

日期實作也是 MockTimers 類別的一部分。請參閱它以取得方法和功能的完整清單。

注意:當一起模擬時,日期和計時器是相依的。這表示如果您同時模擬 DatesetTimeout,則推進時間也會推進模擬日期,因為它們模擬單一的內部時鐘。

以下範例顯示如何模擬 Date 物件並取得目前的 Date.now() 值。

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks the Date object', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'] });
  // If not specified, the initial date will be based on 0 in the UNIX epoch
  assert.strictEqual(Date.now(), 0);

  // Advance in time will also advance the date
  context.mock.timers.tick(9999);
  assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks the Date object', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'] });
  // If not specified, the initial date will be based on 0 in the UNIX epoch
  assert.strictEqual(Date.now(), 0);

  // Advance in time will also advance the date
  context.mock.timers.tick(9999);
  assert.strictEqual(Date.now(), 9999);
});

如果沒有設定初始紀元,初始日期將會根據 Unix 紀元的 0 為基礎。這是 1970 年 1 月 1 日,00:00:00 UTC。你可以透過將 now 屬性傳遞給 .enable() 方法來設定初始日期。這個值將會用作模擬 Date 物件的初始日期。它可以是一個正整數,或另一個 Date 物件。

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks the Date object with initial time', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'], now: 100 });
  assert.strictEqual(Date.now(), 100);

  // Advance in time will also advance the date
  context.mock.timers.tick(200);
  assert.strictEqual(Date.now(), 300);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks the Date object with initial time', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'], now: 100 });
  assert.strictEqual(Date.now(), 100);

  // Advance in time will also advance the date
  context.mock.timers.tick(200);
  assert.strictEqual(Date.now(), 300);
});

你可以使用 .setTime() 方法手動將模擬日期移至另一個時間。此方法只接受正整數。

注意:此方法將會執行任何從新時間開始的模擬計時器。

在以下範例中,我們正在為模擬日期設定一個新時間。

import assert from 'node:assert';
import { test } from 'node:test';

test('sets the time of a date object', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'], now: 100 });
  assert.strictEqual(Date.now(), 100);

  // Advance in time will also advance the date
  context.mock.timers.setTime(1000);
  context.mock.timers.tick(200);
  assert.strictEqual(Date.now(), 1200);
});const assert = require('node:assert');
const { test } = require('node:test');

test('sets the time of a date object', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['Date'], now: 100 });
  assert.strictEqual(Date.now(), 100);

  // Advance in time will also advance the date
  context.mock.timers.setTime(1000);
  context.mock.timers.tick(200);
  assert.strictEqual(Date.now(), 1200);
});

如果你有任何設定為在過去執行的計時器,它將會被執行,就像已經呼叫了 .tick() 方法一樣。如果你想要測試已經在過去的時間依賴功能,這會很有用。

import assert from 'node:assert';
import { test } from 'node:test';

test('runs timers as setTime passes ticks', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const fn = context.mock.fn();
  setTimeout(fn, 1000);

  context.mock.timers.setTime(800);
  // Timer is not executed as the time is not yet reached
  assert.strictEqual(fn.mock.callCount(), 0);
  assert.strictEqual(Date.now(), 800);

  context.mock.timers.setTime(1200);
  // Timer is executed as the time is now reached
  assert.strictEqual(fn.mock.callCount(), 1);
  assert.strictEqual(Date.now(), 1200);
});const assert = require('node:assert');
const { test } = require('node:test');

test('runs timers as setTime passes ticks', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const fn = context.mock.fn();
  setTimeout(fn, 1000);

  context.mock.timers.setTime(800);
  // Timer is not executed as the time is not yet reached
  assert.strictEqual(fn.mock.callCount(), 0);
  assert.strictEqual(Date.now(), 800);

  context.mock.timers.setTime(1200);
  // Timer is executed as the time is now reached
  assert.strictEqual(fn.mock.callCount(), 1);
  assert.strictEqual(Date.now(), 1200);
});

使用 .runAll() 將會執行目前在佇列中的所有計時器。這也會將模擬日期推進到執行最後一個計時器時的時間,就像時間已經過去一樣。

import assert from 'node:assert';
import { test } from 'node:test';

test('runs timers as setTime passes ticks', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const fn = context.mock.fn();
  setTimeout(fn, 1000);
  setTimeout(fn, 2000);
  setTimeout(fn, 3000);

  context.mock.timers.runAll();
  // All timers are executed as the time is now reached
  assert.strictEqual(fn.mock.callCount(), 3);
  assert.strictEqual(Date.now(), 3000);
});const assert = require('node:assert');
const { test } = require('node:test');

test('runs timers as setTime passes ticks', (context) => {
  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const fn = context.mock.fn();
  setTimeout(fn, 1000);
  setTimeout(fn, 2000);
  setTimeout(fn, 3000);

  context.mock.timers.runAll();
  // All timers are executed as the time is now reached
  assert.strictEqual(fn.mock.callCount(), 3);
  assert.strictEqual(Date.now(), 3000);
});

測試報告器#

node:test 模組支援傳遞 --test-reporter 旗標,讓測試執行器使用特定的報告器。

支援下列內建報告器

  • tap tap 報告器以 TAP 格式輸出測試結果。

  • spec spec 報告器以人類可讀的格式輸出測試結果。

  • dot dot 報告器以簡潔的格式輸出測試結果,其中每個通過的測試都以 . 表示,每個失敗的測試都以 X 表示。

  • junit junit 報告器以 jUnit XML 格式輸出測試結果

  • lcov lcov 報告器在搭配 --experimental-test-coverage 旗標使用時,會輸出測試涵蓋率。

stdoutTTY 時,預設會使用 spec 報告器。否則,預設會使用 tap 報告器。

這些報告器的確切輸出可能會在不同版本的 Node.js 之間變更,不應程式化地依賴它們。如果需要以程式化方式存取測試執行器的輸出,請使用 <TestsStream> 所發出的事件。

報告器可透過 node:test/reporters 模組取得

import { tap, spec, dot, junit, lcov } from 'node:test/reporters';const { tap, spec, dot, junit, lcov } = require('node:test/reporters');

自訂報告器#

--test-reporter 可用於指定自訂報告器的路徑。自訂報告器是一個模組,用於匯出 stream.compose 所接受的值。報告器應轉換 <TestsStream> 所發出的事件

使用 <stream.Transform> 的自訂報告器範例

import { Transform } from 'node:stream';

const customReporter = new Transform({
  writableObjectMode: true,
  transform(event, encoding, callback) {
    switch (event.type) {
      case 'test:dequeue':
        callback(null, `test ${event.data.name} dequeued`);
        break;
      case 'test:enqueue':
        callback(null, `test ${event.data.name} enqueued`);
        break;
      case 'test:watch:drained':
        callback(null, 'test watch queue drained');
        break;
      case 'test:start':
        callback(null, `test ${event.data.name} started`);
        break;
      case 'test:pass':
        callback(null, `test ${event.data.name} passed`);
        break;
      case 'test:fail':
        callback(null, `test ${event.data.name} failed`);
        break;
      case 'test:plan':
        callback(null, 'test plan');
        break;
      case 'test:diagnostic':
      case 'test:stderr':
      case 'test:stdout':
        callback(null, event.data.message);
        break;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        callback(null, `total line count: ${totalLineCount}\n`);
        break;
      }
    }
  },
});

export default customReporter;const { Transform } = require('node:stream');

const customReporter = new Transform({
  writableObjectMode: true,
  transform(event, encoding, callback) {
    switch (event.type) {
      case 'test:dequeue':
        callback(null, `test ${event.data.name} dequeued`);
        break;
      case 'test:enqueue':
        callback(null, `test ${event.data.name} enqueued`);
        break;
      case 'test:watch:drained':
        callback(null, 'test watch queue drained');
        break;
      case 'test:start':
        callback(null, `test ${event.data.name} started`);
        break;
      case 'test:pass':
        callback(null, `test ${event.data.name} passed`);
        break;
      case 'test:fail':
        callback(null, `test ${event.data.name} failed`);
        break;
      case 'test:plan':
        callback(null, 'test plan');
        break;
      case 'test:diagnostic':
      case 'test:stderr':
      case 'test:stdout':
        callback(null, event.data.message);
        break;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        callback(null, `total line count: ${totalLineCount}\n`);
        break;
      }
    }
  },
});

module.exports = customReporter;

使用產生器函式的自訂報告器範例

export default async function * customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      case 'test:dequeue':
        yield `test ${event.data.name} dequeued`;
        break;
      case 'test:enqueue':
        yield `test ${event.data.name} enqueued`;
        break;
      case 'test:watch:drained':
        yield 'test watch queue drained';
        break;
      case 'test:start':
        yield `test ${event.data.name} started\n`;
        break;
      case 'test:pass':
        yield `test ${event.data.name} passed\n`;
        break;
      case 'test:fail':
        yield `test ${event.data.name} failed\n`;
        break;
      case 'test:plan':
        yield 'test plan';
        break;
      case 'test:diagnostic':
      case 'test:stderr':
      case 'test:stdout':
        yield `${event.data.message}\n`;
        break;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        yield `total line count: ${totalLineCount}\n`;
        break;
      }
    }
  }
}module.exports = async function * customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      case 'test:dequeue':
        yield `test ${event.data.name} dequeued`;
        break;
      case 'test:enqueue':
        yield `test ${event.data.name} enqueued`;
        break;
      case 'test:watch:drained':
        yield 'test watch queue drained';
        break;
      case 'test:start':
        yield `test ${event.data.name} started\n`;
        break;
      case 'test:pass':
        yield `test ${event.data.name} passed\n`;
        break;
      case 'test:fail':
        yield `test ${event.data.name} failed\n`;
        break;
      case 'test:plan':
        yield 'test plan\n';
        break;
      case 'test:diagnostic':
      case 'test:stderr':
      case 'test:stdout':
        yield `${event.data.message}\n`;
        break;
      case 'test:coverage': {
        const { totalLineCount } = event.data.summary.totals;
        yield `total line count: ${totalLineCount}\n`;
        break;
      }
    }
  }
};

提供給 --test-reporter 的值應為 JavaScript 程式碼中 import() 所使用的字串,或提供給 --import 的值。

多個報告器#

--test-reporter 旗標可指定多次,以多種格式報告測試結果。在這種情況下,必須使用 --test-reporter-destination 為每個報告器指定目的地。目的地可以是 stdoutstderr 或檔案路徑。報告器和目的地會根據指定的順序配對。

在以下範例中,spec 報告器會輸出至 stdout,而 dot 報告器會輸出至 file.txt

node --test-reporter=spec --test-reporter=dot --test-reporter-destination=stdout --test-reporter-destination=file.txt 

當指定單一報告器時,目的地會預設為 stdout,除非明確提供目的地。

run([options])#

  • options <Object> 執行測試的設定選項。支援下列屬性
    • concurrency <number> | <boolean> 如果提供數字,則會平行執行這麼多個測試程序,其中每個程序對應一個測試檔案。如果為 true,則會平行執行 os.availableParallelism() - 1 個測試檔案。如果為 false,則一次只會執行一個測試檔案。預設值:false
    • files: <Array> 包含要執行檔案清單的陣列。預設值測試執行器執行模式 相符的檔案。
    • inspectPort <number> | <Function> 設定測試子程序的檢查埠。這可以是數字,或是不帶參數且傳回數字的函式。如果提供 nullish 值,則每個程序會取得自己的埠,從主要程序的 process.debugPort 開始遞增。預設值:undefined
    • only: <布林值> 如果為真,測試內容只會執行設定 only 選項的測試
    • setup <函式> 接受 TestsStream 執行個體的函式,可以在執行任何測試前用來設定監聽器。預設: 未定義
    • signal <中斷訊號> 允許中斷進行中的測試執行。
    • testNamePatterns <字串> | <正規表示式> | <陣列> 字串、正規表示式或正規表示式陣列,可用於只執行名稱符合所提供模式的測試。測試名稱模式會被解釋為 JavaScript 正規表示式。對於執行的每個測試,任何對應的測試掛勾(例如 beforeEach())也會執行。預設: 未定義
    • timeout <數字> 測試執行將在經過此毫秒數後失敗。如果未指定,子測試會從其父項繼承此值。預設: 無限
    • watch <布林值> 是否以監控模式執行。預設: false
    • shard <物件> 在特定分片中執行測試。預設: 未定義
      • index <數字> 是 1 到 <總計> 之間的正整數,用來指定要執行的分片索引。此選項為必要
      • total <數字> 是正整數,用來指定要將測試檔案分割成多少個分片。此選項為必要
  • 傳回:<TestsStream>

注意: shard 用於橫向並行化跨機器或程序執行測試,非常適合在各種環境中進行大規模執行。它與 watch 模式不相容,後者透過在檔案變更時自動重新執行測試,而適合快速程式碼迭代。

import { tap } from 'node:test/reporters';
import { run } from 'node:test';
import process from 'node:process';
import path from 'node:path';

run({ files: [path.resolve('./tests/test.js')] })
 .on('test:fail', () => {
   process.exitCode = 1;
 })
 .compose(tap)
 .pipe(process.stdout);const { tap } = require('node:test/reporters');
const { run } = require('node:test');
const path = require('node:path');

run({ files: [path.resolve('./tests/test.js')] })
 .on('test:fail', () => {
   process.exitCode = 1;
 })
 .compose(tap)
 .pipe(process.stdout);

test([name][, options][, fn])#

  • name <字串> 測試名稱,會在報告測試結果時顯示。預設: fnname 屬性,如果 fn 沒有名稱,則為 '<匿名>'
  • options <物件> 測試的組態選項。支援下列屬性
    • concurrency <數字> | <布林> 如果提供數字,則會在應用程式執行緒中並行執行那麼多個測試。如果為 true,所有排定的非同步測試會在執行緒中並行執行。如果為 false,一次只會執行一個測試。如果未指定,子測試會從其父項繼承此值。預設: false
    • only <布林> 如果為真,且測試內容設定為只執行 only 測試,則會執行此測試。否則,會略過此測試。預設: false
    • signal <中止訊號> 允許中止正在進行的測試。
    • skip <布林值> | <字串> 如果為真值,則會略過測試。如果提供字串,則會在測試結果中顯示該字串作為略過測試的原因。預設:false
    • todo <布林值> | <字串> 如果為真值,則會將測試標記為 TODO。如果提供字串,則會在測試結果中顯示該字串作為測試為 TODO 的原因。預設:false
    • timeout <數字> 測試會在經過此毫秒數後失敗。如果未指定,子測試會從其父測試繼承此值。預設:Infinity
  • fn <函式> | <非同步函式> 正在測試的函式。此函式的第一個引數是 TestContext 物件。如果測試使用回呼,則會將回呼函式作為第二個引數傳遞。預設:無動作函式。
  • 傳回:<承諾> 在測試完成後以 undefined 履行,或是在 describe() 中執行測試時立即履行。

test() 函式是從 test 模組匯入的值。每次呼叫此函式都會將測試報告給 <TestsStream>

傳遞給 fn 引數的 TestContext 物件可用於執行與目前測試相關的動作。範例包括略過測試、新增額外的診斷資訊,或建立子測試。

test() 會傳回一個承諾,在測試完成後履行。如果在 describe() 區塊中呼叫 test(),它會立即履行。頂層測試的傳回值通常可以捨棄。不過,子測試的傳回值應該用於防止父測試先完成並取消子測試,如下列範例所示。

test('top level test', async (t) => {
  // The setTimeout() in the following subtest would cause it to outlive its
  // parent test if 'await' is removed on the next line. Once the parent test
  // completes, it will cancel any outstanding subtests.
  await t.test('longer running subtest', async (t) => {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, 1000);
    });
  });
}); 

timeout 選項可以用於在執行時間超過 timeout 毫秒時使測試失敗。然而,它並非取消測試的可靠機制,因為執行的測試可能會封鎖應用程式執行緒,從而阻止預定的取消。

test.skip([name][, options][, fn])#

跳過測試的簡寫,與 test([name], { skip: true }[, fn]) 相同。

test.todo([name][, options][, fn])#

將測試標記為 TODO 的簡寫,與 test([name], { todo: true }[, fn]) 相同。

test.only([name][, options][, fn])#

將測試標記為 only 的簡寫,與 test([name], { only: true }[, fn]) 相同。

describe([name][, options][, fn])#

  • name <string> 套件的名稱,在報告測試結果時顯示。預設值:fnname 屬性,如果 fn 沒有名稱,則為 '<anonymous>'
  • options <Object> 套件的設定選項。支援與 test([name][, options][, fn]) 相同的選項。
  • fn <Function> | <AsyncFunction> 套件下的函式,宣告所有子測試和子套件。此函式的第一個引數是 SuiteContext 物件。預設值:無操作函式。
  • 傳回:<Promise> 立即完成,值為 undefined

node:test 模組匯入的 describe() 函式。每次呼叫此函式都會建立一個子測試。在呼叫頂層 describe 函式後,所有頂層測試和套件都會執行。

describe.skip([name][, options][, fn])#

跳過測試組的簡寫,與 describe([name], { skip: true }[, fn]) 相同。

describe.todo([name][, options][, fn])#

將測試組標記為 TODO 的簡寫,與 describe([name], { todo: true }[, fn]) 相同。

describe.only([name][, options][, fn])#

將測試組標記為 only 的簡寫,與 describe([name], { only: true }[, fn]) 相同。

it([name][, options][, fn])#

test() 的簡寫。

it() 函式從 node:test 模組匯入。

it.skip([name][, options][, fn])#

跳過測試的簡寫,與 it([name], { skip: true }[, fn]) 相同。

it.todo([name][, options][, fn])#

將測試標記為 TODO 的簡寫,與 it([name], { todo: true }[, fn]) 相同。

it.only([name][, options][, fn])#

將測試標記為 only 的簡寫,與 it([name], { only: true }[, fn]) 相同。

before([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。如果掛鉤使用回呼,則回呼函式會傳遞為第二個參數。預設值:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於在執行套件之前建立一個掛鉤。

describe('tests', async () => {
  before(() => console.log('about to run some test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
}); 

after([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。如果掛鉤使用回呼,則回呼函式會傳遞為第二個參數。預設值:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於在執行套件之後建立一個掛鉤。

describe('tests', async () => {
  after(() => console.log('finished running tests'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
}); 

注意:即使套件中的測試失敗,after 掛鉤仍會執行。

beforeEach([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。如果掛鉤使用回呼,則回呼函式會傳遞為第二個參數。預設值:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於在執行目前套件的每個子測試之前建立一個掛鉤。

describe('tests', async () => {
  beforeEach(() => console.log('about to run a test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
}); 

afterEach([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。如果掛鉤使用回呼,則回呼函式會傳遞為第二個參數。預設值:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於在執行目前測試的每個子測試之後建立一個掛鉤。

注意:即使任何測試失敗,afterEach 掛鉤仍會在每個測試後執行。

describe('tests', async () => {
  afterEach(() => console.log('finished running a test'));
  it('is a subtest', () => {
    assert.ok('some relevant assertion here');
  });
}); 

類別:MockFunctionContext#

MockFunctionContext 類別用於檢查或操作透過 MockTracker API 建立的模擬行為。

ctx.calls#

取得用於追蹤對模擬進行呼叫的內部陣列副本的 getter。陣列中的每個項目都是具有下列屬性的物件。

  • arguments <陣列> 傳遞給模擬函式的引數陣列。
  • error <any> 如果模擬函式引發例外狀況,此屬性會包含引發的值。預設值:undefined
  • result <any> 模擬函式傳回的值。
  • stack <Error> Error 物件,其堆疊可 used 用於判斷模擬函式呼叫的呼叫位置。
  • target <函式> | <undefined> 如果模擬函式是建構函式,此欄位會包含正在建構的類別。否則,此欄位會是 undefined
  • this <any> 模擬函式的 this 值。

ctx.callCount()#

  • 傳回:<整數> 呼叫此模擬的次數。

此函式傳回呼叫此模擬的次數。此函式比檢查 ctx.calls.length 更有效率,因為 ctx.calls 是建立內部呼叫追蹤陣列副本的 getter。

ctx.mockImplementation(implementation)#

此函式用於變更現有模擬的行為。

下列範例使用 t.mock.fn() 建立模擬函式,呼叫模擬函式,然後將模擬實作變更為不同的函式。

test('changes a mock behavior', (t) => {
  let cnt = 0;

  function addOne() {
    cnt++;
    return cnt;
  }

  function addTwo() {
    cnt += 2;
    return cnt;
  }

  const fn = t.mock.fn(addOne);

  assert.strictEqual(fn(), 1);
  fn.mock.mockImplementation(addTwo);
  assert.strictEqual(fn(), 3);
  assert.strictEqual(fn(), 5);
}); 

ctx.mockImplementationOnce(implementation[, onCall])#

  • implementation <Function> | <AsyncFunction> 要用作模擬實作的函式,呼叫次數由 onCall 指定。
  • onCall <integer> 將使用 implementation 的呼叫次數。如果指定的呼叫已經發生,則會擲回例外。預設值:下一次呼叫的次數。

此函式用於變更現有模擬的行為,僅限一次呼叫。一旦呼叫 onCall 發生,模擬將恢復為未呼叫 mockImplementationOnce() 時的行為。

下列範例使用 t.mock.fn() 建立模擬函式,呼叫模擬函式,將模擬實作變更為不同的函式,供下次呼叫使用,然後繼續執行先前的行為。

test('changes a mock behavior once', (t) => {
  let cnt = 0;

  function addOne() {
    cnt++;
    return cnt;
  }

  function addTwo() {
    cnt += 2;
    return cnt;
  }

  const fn = t.mock.fn(addOne);

  assert.strictEqual(fn(), 1);
  fn.mock.mockImplementationOnce(addTwo);
  assert.strictEqual(fn(), 3);
  assert.strictEqual(fn(), 4);
}); 

ctx.resetCalls()#

重設模擬函數的呼叫記錄。

ctx.restore()#

重設模擬函數的實作至其原始行為。呼叫這個函數後,模擬仍可使用。

類別:MockTracker#

MockTracker 類別用於管理模擬功能。測試執行器模組提供頂層 mock 匯出,它是一個 MockTracker 執行個體。每個測試也透過測試內容的 mock 屬性提供它自己的 MockTracker 執行個體。

mock.fn([original[, implementation]][, options])#

  • original <Function> | <AsyncFunction> 一個用於建立模擬的選用函數。預設:一個空操作函數。
  • implementation <Function> | <AsyncFunction> 一個選用函數,用作 original 的模擬實作。這對於建立在指定呼叫次數內表現出一種行為,然後還原 original 行為的模擬很有用。預設:original 指定的函數。
  • options <Object> 模擬函數的選用組態選項。支援下列屬性
    • times <integer> 模擬將使用 implementation 行為的次數。模擬函數被呼叫 times 次後,它將自動還原 original 的行為。此值必須大於 0 的整數。預設:Infinity
  • 傳回:<Proxy> 模擬函數。模擬函數包含一個特殊的 mock 屬性,它是 MockFunctionContext 的執行個體,可用於檢查和變更模擬函數的行為。

這個函數用於建立模擬函數。

以下範例建立一個模擬函式,每次呼叫都會將計數器增加一。times 選項用於修改模擬行為,讓前兩次呼叫將計數器增加兩,而非一。

test('mocks a counting function', (t) => {
  let cnt = 0;

  function addOne() {
    cnt++;
    return cnt;
  }

  function addTwo() {
    cnt += 2;
    return cnt;
  }

  const fn = t.mock.fn(addOne, addTwo, { times: 2 });

  assert.strictEqual(fn(), 2);
  assert.strictEqual(fn(), 4);
  assert.strictEqual(fn(), 5);
  assert.strictEqual(fn(), 6);
}); 

mock.getter(object, methodName[, implementation][, options])#

此函式是 MockTracker.method 的語法糖,其中 options.getter 設為 true

mock.method(object, methodName[, implementation][, options])#

  • object <Object> 要模擬其方法的物件。
  • methodName <string> | <symbol> 要在 object 上模擬的方法識別碼。如果 object[methodName] 不是函式,會擲回錯誤。
  • implementation <Function> | <AsyncFunction> 用作 object[methodName] 的模擬實作的選用函式。預設值:object[methodName] 指定的原始方法。
  • options <Object> 模擬方法的選用設定選項。支援下列屬性
    • getter <boolean> 如果為 trueobject[methodName] 會被視為 getter。此選項無法與 setter 選項搭配使用。預設值:false。
    • setter <boolean> 如果為 trueobject[methodName] 會被視為 setter。此選項無法與 getter 選項搭配使用。預設值:false。
    • times <整數> 模擬將使用 implementation 行為的次數。在模擬方法被呼叫 times 次後,它將自動還原原始行為。此值必須大於零的整數。預設:Infinity
  • 傳回: <Proxy> 模擬方法。模擬方法包含一個特殊的 mock 屬性,它是 MockFunctionContext 的一個實例,可用於檢查和變更模擬方法的行為。

此函式用於在現有物件方法上建立一個模擬。以下範例說明如何於現有物件方法上建立模擬。

test('spies on an object method', (t) => {
  const number = {
    value: 5,
    subtract(a) {
      return this.value - a;
    },
  };

  t.mock.method(number, 'subtract');
  assert.strictEqual(number.subtract.mock.calls.length, 0);
  assert.strictEqual(number.subtract(3), 2);
  assert.strictEqual(number.subtract.mock.calls.length, 1);

  const call = number.subtract.mock.calls[0];

  assert.deepStrictEqual(call.arguments, [3]);
  assert.strictEqual(call.result, 2);
  assert.strictEqual(call.error, undefined);
  assert.strictEqual(call.target, undefined);
  assert.strictEqual(call.this, number);
}); 

mock.reset()#

此函式還原先前由這個 MockTracker 建立的所有模擬的預設行為,並解除模擬與 MockTracker 實例的關聯。解除關聯後,模擬仍然可以使用,但 MockTracker 實例無法再用於重設其行為或與其互動。

在每個測試完成後,此函式會在測試內容的 MockTracker 上呼叫。如果大量使用全域 MockTracker,建議手動呼叫此函式。

mock.restoreAll()#

此函式還原先前由這個 MockTracker 建立的所有模擬的預設行為。與 mock.reset() 不同,mock.restoreAll() 沒有解除模擬與 MockTracker 實例的關聯。

mock.setter(object, methodName[, implementation][, options])#

此函數是 MockTracker.method 的語法糖,其中 options.setter 設為 true

類別:MockTimers#

穩定性:1 - 實驗性

模擬計時器是軟體測試中常用的技術,用於模擬和控制計時器的行為,例如 setIntervalsetTimeout,而無需實際等待指定的時間間隔。

MockTimers 也能模擬 Date 物件。

MockTracker 提供頂層 timers 匯出,它是 MockTimers 執行個體。

timers.enable([enableOptions])#

針對指定的計時器啟用計時器模擬。

  • enableOptions <Object> 啟用計時器模擬的選用組態選項。支援下列屬性
    • apis <Array> 包含要模擬的計時器的選用陣列。目前支援的計時器值為 'setInterval''setTimeout''setImmediate''Date'預設:['setInterval', 'setTimeout', 'setImmediate', 'Date']。如果未提供陣列,預設會模擬所有時間相關 API ('setInterval''clearInterval''setTimeout''clearTimeout''Date')。
    • now <數字> | <日期> 表示要作為 Date.now() 值使用的初始時間(以毫秒為單位)的數字或日期物件(選用)。預設:0

注意:當您啟用特定計時器的模擬時,其關聯的清除函式也會被隱含地模擬。

注意:模擬 Date 會影響模擬計時器的行為,因為它們使用相同的內部時鐘。

不設定初始時間的範例用法

import { mock } from 'node:test';
mock.timers.enable({ apis: ['setInterval'] });const { mock } = require('node:test');
mock.timers.enable({ apis: ['setInterval'] });

上述範例啟用 setInterval 計時器的模擬,並隱含地模擬 clearInterval 函式。只有來自 node:timersnode:timers/promisesglobalThissetIntervalclearInterval 函式會被模擬。

設定初始時間的範例用法

import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: 1000 });const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: 1000 });

設定初始日期物件作為時間的範例用法

import { mock } from 'node:test';
mock.timers.enable({ apis: ['Date'], now: new Date() });const { mock } = require('node:test');
mock.timers.enable({ apis: ['Date'], now: new Date() });

或者,如果您在沒有任何參數的情況下呼叫 mock.timers.enable()

所有計時器('setInterval''clearInterval''setTimeout''clearTimeout')都會被模擬。來自 node:timersnode:timers/promisesglobalThissetIntervalclearIntervalsetTimeoutclearTimeout 函式會被模擬。以及全域 Date 物件。

timers.reset()#

此函式會還原先前由這個 MockTimers 實例建立的所有模擬的預設行為,並解除模擬與 MockTracker 實例的關聯。

注意:在每個測試完成後,此函式會在測試內容的 MockTracker 上呼叫。

import { mock } from 'node:test';
mock.timers.reset();const { mock } = require('node:test');
mock.timers.reset();

timers[Symbol.dispose]()#

呼叫 timers.reset()

timers.tick(milliseconds)#

為所有模擬計時器推進時間。

  • milliseconds <number> 以毫秒為單位的時間量,用於推進計時器。

注意:這與 Node.js 中 setTimeout 的行為不同,且僅接受正數。在 Node.js 中,僅出於網頁相容性原因才支援帶有負數的 setTimeout

以下範例模擬 setTimeout 函式,並透過使用 .tick 推進時間,觸發所有待處理計時器。

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  context.mock.timers.enable({ apis: ['setTimeout'] });

  setTimeout(fn, 9999);

  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  context.mock.timers.tick(9999);

  assert.strictEqual(fn.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();
  context.mock.timers.enable({ apis: ['setTimeout'] });

  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);

  // Advance in time
  context.mock.timers.tick(9999);

  assert.strictEqual(fn.mock.callCount(), 1);
});

或者,可以多次呼叫 .tick 函式

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();
  context.mock.timers.enable({ apis: ['setTimeout'] });
  const nineSecs = 9000;
  setTimeout(fn, nineSecs);

  const twoSeconds = 3000;
  context.mock.timers.tick(twoSeconds);
  context.mock.timers.tick(twoSeconds);
  context.mock.timers.tick(twoSeconds);

  assert.strictEqual(fn.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();
  context.mock.timers.enable({ apis: ['setTimeout'] });
  const nineSecs = 9000;
  setTimeout(fn, nineSecs);

  const twoSeconds = 3000;
  context.mock.timers.tick(twoSeconds);
  context.mock.timers.tick(twoSeconds);
  context.mock.timers.tick(twoSeconds);

  assert.strictEqual(fn.mock.callCount(), 1);
});

使用 .tick 推進時間也會推進在啟用模擬之後建立的任何 Date 物件的時間(如果 Date 也設定為模擬)。

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  setTimeout(fn, 9999);

  assert.strictEqual(fn.mock.callCount(), 0);
  assert.strictEqual(Date.now(), 0);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);
  assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });

  setTimeout(fn, 9999);
  assert.strictEqual(fn.mock.callCount(), 0);
  assert.strictEqual(Date.now(), 0);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(fn.mock.callCount(), 1);
  assert.strictEqual(Date.now(), 9999);
});
使用清除函式#

如前所述,計時器的所有清除函式(clearTimeoutclearInterval)都隱含模擬。請查看使用 setTimeout 的這個範例

import assert from 'node:assert';
import { test } from 'node:test';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  const id = setTimeout(fn, 9999);

  // Implicity mocked as well
  clearTimeout(id);
  context.mock.timers.tick(9999);

  // As that setTimeout was cleared the mock function will never be called
  assert.strictEqual(fn.mock.callCount(), 0);
});const assert = require('node:assert');
const { test } = require('node:test');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', (context) => {
  const fn = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  const id = setTimeout(fn, 9999);

  // Implicity mocked as well
  clearTimeout(id);
  context.mock.timers.tick(9999);

  // As that setTimeout was cleared the mock function will never be called
  assert.strictEqual(fn.mock.callCount(), 0);
});
使用 Node.js 計時器模組#

啟用模擬計時器後,node:timersnode:timers/promises 模組,以及來自 Node.js 全域環境的計時器都會被啟用

注意:此 API 目前不支援解構函式,例如 import { setTimeout } from 'node:timers'

import assert from 'node:assert';
import { test } from 'node:test';
import nodeTimers from 'node:timers';
import nodeTimersPromises from 'node:timers/promises';

test('mocks setTimeout to be executed synchronously without having to actually wait for it', async (context) => {
  const globalTimeoutObjectSpy = context.mock.fn();
  const nodeTimerSpy = context.mock.fn();
  const nodeTimerPromiseSpy = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(globalTimeoutObjectSpy, 9999);
  nodeTimers.setTimeout(nodeTimerSpy, 9999);

  const promise = nodeTimersPromises.setTimeout(9999).then(nodeTimerPromiseSpy);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(globalTimeoutObjectSpy.mock.callCount(), 1);
  assert.strictEqual(nodeTimerSpy.mock.callCount(), 1);
  await promise;
  assert.strictEqual(nodeTimerPromiseSpy.mock.callCount(), 1);
});const assert = require('node:assert');
const { test } = require('node:test');
const nodeTimers = require('node:timers');
const nodeTimersPromises = require('node:timers/promises');

test('mocks setTimeout to be executed synchronously without having to actually wait for it', async (context) => {
  const globalTimeoutObjectSpy = context.mock.fn();
  const nodeTimerSpy = context.mock.fn();
  const nodeTimerPromiseSpy = context.mock.fn();

  // Optionally choose what to mock
  context.mock.timers.enable({ apis: ['setTimeout'] });
  setTimeout(globalTimeoutObjectSpy, 9999);
  nodeTimers.setTimeout(nodeTimerSpy, 9999);

  const promise = nodeTimersPromises.setTimeout(9999).then(nodeTimerPromiseSpy);

  // Advance in time
  context.mock.timers.tick(9999);
  assert.strictEqual(globalTimeoutObjectSpy.mock.callCount(), 1);
  assert.strictEqual(nodeTimerSpy.mock.callCount(), 1);
  await promise;
  assert.strictEqual(nodeTimerPromiseSpy.mock.callCount(), 1);
});

在 Node.js 中,node:timers/promises 中的 setIntervalAsyncGenerator,而且也受到此 API 支援

import assert from 'node:assert';
import { test } from 'node:test';
import nodeTimersPromises from 'node:timers/promises';
test('should tick five times testing a real use case', async (context) => {
  context.mock.timers.enable({ apis: ['setInterval'] });

  const expectedIterations = 3;
  const interval = 1000;
  const startedAt = Date.now();
  async function run() {
    const times = [];
    for await (const time of nodeTimersPromises.setInterval(interval, startedAt)) {
      times.push(time);
      if (times.length === expectedIterations) break;
    }
    return times;
  }

  const r = run();
  context.mock.timers.tick(interval);
  context.mock.timers.tick(interval);
  context.mock.timers.tick(interval);

  const timeResults = await r;
  assert.strictEqual(timeResults.length, expectedIterations);
  for (let it = 1; it < expectedIterations; it++) {
    assert.strictEqual(timeResults[it - 1], startedAt + (interval * it));
  }
});const assert = require('node:assert');
const { test } = require('node:test');
const nodeTimersPromises = require('node:timers/promises');
test('should tick five times testing a real use case', async (context) => {
  context.mock.timers.enable({ apis: ['setInterval'] });

  const expectedIterations = 3;
  const interval = 1000;
  const startedAt = Date.now();
  async function run() {
    const times = [];
    for await (const time of nodeTimersPromises.setInterval(interval, startedAt)) {
      times.push(time);
      if (times.length === expectedIterations) break;
    }
    return times;
  }

  const r = run();
  context.mock.timers.tick(interval);
  context.mock.timers.tick(interval);
  context.mock.timers.tick(interval);

  const timeResults = await r;
  assert.strictEqual(timeResults.length, expectedIterations);
  for (let it = 1; it < expectedIterations; it++) {
    assert.strictEqual(timeResults[it - 1], startedAt + (interval * it));
  }
});

timers.runAll()#

立即觸發所有待處理的模擬計時器。如果 Date 物件也模擬,它也會將 Date 物件推進到最遠的計時器時間。

以下範例立即觸發所有待處理計時器,導致它們在沒有任何延遲的情況下執行。

import assert from 'node:assert';
import { test } from 'node:test';

test('runAll functions following the given order', (context) => {
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const results = [];
  setTimeout(() => results.push(1), 9999);

  // Notice that if both timers have the same timeout,
  // the order of execution is guaranteed
  setTimeout(() => results.push(3), 8888);
  setTimeout(() => results.push(2), 8888);

  assert.deepStrictEqual(results, []);

  context.mock.timers.runAll();
  assert.deepStrictEqual(results, [3, 2, 1]);
  // The Date object is also advanced to the furthest timer's time
  assert.strictEqual(Date.now(), 9999);
});const assert = require('node:assert');
const { test } = require('node:test');

test('runAll functions following the given order', (context) => {
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const results = [];
  setTimeout(() => results.push(1), 9999);

  // Notice that if both timers have the same timeout,
  // the order of execution is guaranteed
  setTimeout(() => results.push(3), 8888);
  setTimeout(() => results.push(2), 8888);

  assert.deepStrictEqual(results, []);

  context.mock.timers.runAll();
  assert.deepStrictEqual(results, [3, 2, 1]);
  // The Date object is also advanced to the furthest timer's time
  assert.strictEqual(Date.now(), 9999);
});

注意:runAll() 函式專門設計用於在計時器模擬的環境中觸發計時器。它不會對模擬環境之外的實際系統時鐘或實際計時器產生任何影響。

timers.setTime(milliseconds)#

設定目前的 Unix 時間戳記,將用作任何模擬的 Date 物件的參考。

import assert from 'node:assert';
import { test } from 'node:test';

test('runAll functions following the given order', (context) => {
  const now = Date.now();
  const setTime = 1000;
  // Date.now is not mocked
  assert.deepStrictEqual(Date.now(), now);

  context.mock.timers.enable({ apis: ['Date'] });
  context.mock.timers.setTime(setTime);
  // Date.now is now 1000
  assert.strictEqual(Date.now(), setTime);
});const assert = require('node:assert');
const { test } = require('node:test');

test('setTime replaces current time', (context) => {
  const now = Date.now();
  const setTime = 1000;
  // Date.now is not mocked
  assert.deepStrictEqual(Date.now(), now);

  context.mock.timers.enable({ apis: ['Date'] });
  context.mock.timers.setTime(setTime);
  // Date.now is now 1000
  assert.strictEqual(Date.now(), setTime);
});
日期和計時器共同運作#

日期和計時器物件彼此依賴。如果您使用 setTime() 將目前時間傳遞給模擬的 Date 物件,則使用 setTimeoutsetInterval 設定的計時器不會受到影響。

不過,tick 方法推進模擬的 Date 物件。

import assert from 'node:assert';
import { test } from 'node:test';

test('runAll functions following the given order', (context) => {
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const results = [];
  setTimeout(() => results.push(1), 9999);

  assert.deepStrictEqual(results, []);
  context.mock.timers.setTime(12000);
  assert.deepStrictEqual(results, []);
  // The date is advanced but the timers don't tick
  assert.strictEqual(Date.now(), 12000);
});const assert = require('node:assert');
const { test } = require('node:test');

test('runAll functions following the given order', (context) => {
  context.mock.timers.enable({ apis: ['setTimeout', 'Date'] });
  const results = [];
  setTimeout(() => results.push(1), 9999);

  assert.deepStrictEqual(results, []);
  context.mock.timers.setTime(12000);
  assert.deepStrictEqual(results, []);
  // The date is advanced but the timers don't tick
  assert.strictEqual(Date.now(), 12000);
});

類別:TestsStream#

成功呼叫 run() 方法將傳回新的 <TestsStream> 物件,串流一系列代表測試執行的事件。TestsStream 將發出事件,依據測試定義的順序

事件:'test:coverage'#

  • data <Object>
    • summary <Object>包含涵蓋率報告的物件。
      • files <陣列> 各檔案的涵蓋率報告陣列。每個報告都是一個具有下列結構的物件
        • path <字串> 檔案的絕對路徑。
        • totalLineCount <數字> 總行數。
        • totalBranchCount <數字> 總分支數。
        • totalFunctionCount <數字> 總函式數。
        • coveredLineCount <數字> 涵蓋的行數。
        • coveredBranchCount <數字> 涵蓋的分支數。
        • coveredFunctionCount <數字> 涵蓋的函式數。
        • coveredLinePercent <數字> 涵蓋率百分比。
        • coveredBranchPercent <數字> 分支涵蓋率百分比。
        • coveredFunctionPercent <數字> 函式涵蓋率百分比。
        • functions <陣列> 表示函式涵蓋率的函式陣列。
        • branches <陣列> 表示分支涵蓋率的分支陣列。
          • line <數字> 定義分支的行號。
          • count <數字> 分支執行的次數。
        • lines <陣列> 代表行號和執行次數的陣列。
      • totals <物件> 包含所有檔案涵蓋率摘要的物件。
        • totalLineCount <數字> 總行數。
        • totalBranchCount <數字> 總分支數。
        • totalFunctionCount <數字> 總函式數。
        • coveredLineCount <數字> 涵蓋的行數。
        • coveredBranchCount <數字> 涵蓋的分支數。
        • coveredFunctionCount <數字> 涵蓋的函式數。
        • coveredLinePercent <數字> 涵蓋率百分比。
        • coveredBranchPercent <數字> 分支涵蓋率百分比。
        • coveredFunctionPercent <數字> 函式涵蓋率百分比。
      • workingDirectory <字串> 開始程式碼涵蓋率時的作業目錄。這有助於顯示相對路徑名稱,以防測試變更 Node.js 程序的作業目錄。
    • nesting <數字> 測試的巢狀層級。

在啟用程式碼涵蓋率且所有測試都已完成時發出。

事件:'test:dequeue'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <字串> | <未定義> 測試檔案的路徑,如果測試是透過 REPL 執行的,則為 未定義
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • name <字串> 測試名稱。
    • nesting <數字> 測試的巢狀層級。

在測試出列,準備執行之前發出。

事件:'test:diagnostic'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <字串> | <未定義> 測試檔案的路徑,如果測試是透過 REPL 執行的,則為 未定義
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • message <字串> 診斷訊息。
    • nesting <數字> 測試的巢狀層級。

在呼叫 context.diagnostic 時發出。

事件:'test:enqueue'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <字串> | <未定義> 測試檔案的路徑,如果測試是透過 REPL 執行的,則為 未定義
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • name <字串> 測試名稱。
    • nesting <數字> 測試的巢狀層級。

在測試排隊準備執行時發出。

事件:'test:fail'#

在測試失敗時發出。

事件:'test:pass'#

在測試通過時發出。

事件:'test:plan'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <字串> | <未定義> 測試檔案的路徑,如果測試是透過 REPL 執行的,則為 未定義
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • nesting <數字> 測試的巢狀層級。
    • count <數字> 已執行的子測試數量。

在給定測試的所有子測試完成時發出。

事件:'test:start'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <字串> | <未定義> 測試檔案的路徑,如果測試是透過 REPL 執行的,則為 未定義
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • name <字串> 測試名稱。
    • nesting <數字> 測試的巢狀層級。

在測試開始回報其自身及其子測試狀態時發出。保證此事件會按照測試定義的順序發出。

事件:'test:stderr'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <string> 測試檔案的路徑。
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • message <string> 寫入 stderr 的訊息。

當執行的測試寫入 stderr 時發出。只有在傳遞 --test 旗標時才會發出此事件。

事件:'test:stdout'#

  • data <Object>
    • column <數字> | <未定義> 定義測試的欄位號碼,或如果測試是透過 REPL 執行的,則為 未定義
    • file <string> 測試檔案的路徑。
    • line <數字> | <未定義> 定義測試的行號,或如果測試是透過 REPL 執行,則為 undefined
    • message <string> 寫入 stdout 的訊息。

當執行的測試寫入 stdout 時發出。只有在傳遞 --test 旗標時才會發出此事件。

事件:'test:watch:drained'#

在監控模式中沒有更多測試排隊執行時發出。

類別:TestContext#

TestContext 的執行個體會傳遞給每個測試函式,以與測試執行器互動。不過,TestContext 建構函式並未公開為 API 的一部分。

context.before([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。此函式的第一個引數是 TestContext 物件。如果掛鉤使用回呼,回呼函式會傳遞為第二個引數。預設:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於建立在目前測試的子測試執行前執行的掛鉤。

context.beforeEach([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。此函式的第一個引數是 TestContext 物件。如果掛鉤使用回呼,回呼函式會傳遞為第二個引數。預設:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於建立在目前測試的每個子測試執行前執行的掛鉤。

test('top level test', async (t) => {
  t.beforeEach((t) => t.diagnostic(`about to run ${t.name}`));
  await t.test(
    'This is a subtest',
    (t) => {
      assert.ok('some relevant assertion here');
    },
  );
}); 

context.after([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。此函式的第一個引數是 TestContext 物件。如果掛鉤使用回呼,回呼函式會傳遞為第二個引數。預設:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於建立在目前測試完成後執行的掛鉤。

test('top level test', async (t) => {
  t.after((t) => t.diagnostic(`finished running ${t.name}`));
  assert.ok('some relevant assertion here');
}); 

context.afterEach([fn][, options])#

  • fn <Function> | <AsyncFunction> 掛鉤函式。此函式的第一個引數是 TestContext 物件。如果掛鉤使用回呼,回呼函式會傳遞為第二個引數。預設:無操作函式。
  • options <Object> 掛鉤的設定選項。支援下列屬性
    • signal <AbortSignal> 允許中止進行中的掛鉤。
    • timeout <number> 掛鉤失敗前的毫秒數。如果未指定,子測試會從其父層繼承此值。預設值:Infinity

此函式用於在執行目前測試的每個子測試之後建立一個掛鉤。

test('top level test', async (t) => {
  t.afterEach((t) => t.diagnostic(`finished running ${t.name}`));
  await t.test(
    'This is a subtest',
    (t) => {
      assert.ok('some relevant assertion here');
    },
  );
}); 

context.diagnostic(message)#

  • message <string> 要報告的訊息。

此函式用於將診斷寫入輸出。任何診斷資訊都會包含在測試結果的結尾。此函式不會傳回值。

test('top level test', (t) => {
  t.diagnostic('A diagnostic message');
}); 

context.name#

測試名稱。

context.runOnly(shouldRunOnlyTests)#

  • shouldRunOnlyTests <boolean> 是否執行only 測試。

如果 shouldRunOnlyTests 為真,測試內容將只執行設定了 only 選項的測試。否則,將執行所有測試。如果 Node.js 未使用 --test-only 命令列選項啟動,此函式將不會執行任何操作。

test('top level test', (t) => {
  // The test context can be set to run subtests with the 'only' option.
  t.runOnly(true);
  return Promise.all([
    t.test('this subtest is now skipped'),
    t.test('this subtest is run', { only: true }),
  ]);
}); 

context.signal#

當測試已中止時,可用於中止測試子任務。

test('top level test', async (t) => {
  await fetch('some/uri', { signal: t.signal });
}); 

context.skip([message])#

  • message <string> 選擇性跳過訊息。

此函式會使測試輸出顯示測試已跳過。如果提供 message,則會包含在輸出中。呼叫 skip() 並不會終止測試函式的執行。此函式不會傳回值。

test('top level test', (t) => {
  // Make sure to return here as well if the test contains additional logic.
  t.skip('this is skipped');
}); 

context.todo([message])#

  • message <string> 選擇性 TODO 訊息。

此函式會將 TODO 指示新增至測試輸出。如果提供 message,則會包含在輸出中。呼叫 todo() 並不會終止測試函式的執行。此函式不會傳回值。

test('top level test', (t) => {
  // This test is marked as `TODO`
  t.todo('this is a todo');
}); 

context.test([name][, options][, fn])#

  • name <string> 子測試的名稱,會在報告測試結果時顯示。預設值:fnname 屬性,或如果 fn 沒有名稱,則為 '<anonymous>'
  • options <Object> 子測試的設定選項。支援下列屬性
    • concurrency <number> | <boolean> | <null> 如果提供數字,那麼應用程式執行緒中將並行執行這麼多個測試。如果為 true,則會並行執行所有子測試。如果為 false,則一次只會執行一個測試。如果未指定,子測試會從其父項繼承此值。預設:null
    • only <布林> 如果為真,且測試內容設定為只執行 only 測試,則會執行此測試。否則,會略過此測試。預設: false
    • signal <中止訊號> 允許中止正在進行的測試。
    • skip <布林值> | <字串> 如果為真值,則會略過測試。如果提供字串,則會在測試結果中顯示該字串作為略過測試的原因。預設:false
    • todo <布林值> | <字串> 如果為真值,則會將測試標記為 TODO。如果提供字串,則會在測試結果中顯示該字串作為測試為 TODO 的原因。預設:false
    • timeout <數字> 測試會在經過此毫秒數後失敗。如果未指定,子測試會從其父測試繼承此值。預設:Infinity
  • fn <函式> | <非同步函式> 正在測試的函式。此函式的第一個引數是 TestContext 物件。如果測試使用回呼,則會將回呼函式作為第二個引數傳遞。預設:無動作函式。
  • 傳回:<Promise> 一旦測試完成,就會以 undefined 履行。

此函式用於在目前測試中建立子測試。此函式的行為與頂層 test() 函式相同。

test('top level test', async (t) => {
  await t.test(
    'This is a subtest',
    { only: false, skip: false, concurrency: 1, todo: false },
    (t) => {
      assert.ok('some relevant assertion here');
    },
  );
}); 

類別:SuiteContext#

SuiteContext 的執行個體會傳遞給每個套件函式,以便與測試執行器互動。但是,SuiteContext 建構函式並未公開為 API 的一部分。

context.name#

套件的名稱。

context.signal#

當測試已中止時,可用於中止測試子任務。