テストランナー#

安定性: 2 - Stable

ソースコード: lib/test.js

node:test モジュールは、JavaScriptテストの作成を容易にします。アクセスするには、次のようにします。

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

このモジュールは node: スキームでのみ利用可能です。

test モジュールを介して作成されたテストは、単一の関数で構成され、以下の3つの方法のいずれかで処理されます。

  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() 関数とまったく同じように動作します。次の例は、2つのサブテストを持つトップレベルのテストの作成を示しています。

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 が使用されています。これは、テストがスイート内で作成されたテストとは異なり、サブテストの完了を待たないためです。親が終了した時点でまだ未完了のサブテストはキャンセルされ、失敗として扱われます。サブテストが失敗すると、親テストも失敗します。

テストのスキップ#

個々のテストは、テストに 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');
}); 

失敗したテストの再実行#

テストランナーは、実行状態をファイルに永続化することをサポートしており、これによりテストスイート全体を再実行することなく、失敗したテストのみを再実行できます。--test-rerun-failures コマンドラインオプションを使用して、実行状態が保存されるファイルパスを指定します。状態ファイルが存在しない場合、テストランナーはそれを作成します。状態ファイルは、実行試行の配列を含むJSONファイルです。各実行試行は、成功したテストがどの試行で成功したかをマッピングするオブジェクトです。このマップでテストを識別するキーは、テストが定義されている行と列を含むテストファイルパスです。特定の場所で定義されたテストが、例えば関数やループ内で複数回実行される場合、テスト実行を明確にするためにキーにカウンターが追加されます。テストの実行順序やテストの場所を変更すると、テストランナーがテストを以前の試行で成功したと見なす可能性があることに注意してください。つまり、--test-rerun-failures は、テストが決定論的な順序で実行される場合に使用する必要があります。

状態ファイルの例

[
  {
    "test.js:10:5": { "passed_on_attempt": 0, "name": "test 1" },
  },
  {
    "test.js:10:5": { "passed_on_attempt": 0, "name": "test 1" },
    "test.js:20:5": { "passed_on_attempt": 1, "name": "test 2" }
  }
] 

この例では、2回の実行試行があり、test.js に2つのテストが定義されています。最初のテストは最初の試行で成功し、2番目のテストは2番目の試行で成功しました。

--test-rerun-failures オプションが使用されると、テストランナーはまだ成功していないテストのみを実行します。

node --test-rerun-failures /path/to/state/file 

TODOテスト#

個々のテストは、テストに todo オプションを渡すか、次の例に示すようにテストコンテキストの todo() メソッドを呼び出すことで、不安定または未完了としてマークできます。これらのテストは、保留中の実装または修正が必要なバグを表します。TODOテストは実行されますが、テストの失敗としては扱われないため、プロセスの終了コードには影響しません。テストがTODOとスキップの両方でマークされている場合、TODOオプションは無視されます。

// The todo option is used, but no message is provided.
test('todo option', { todo: true }, (t) => {
  // This code is executed, but not treated as a failure.
  throw new Error('this does not fail the test');
});

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

test('todo() method', (t) => {
  t.todo();
});

test('todo() method with message', (t) => {
  t.todo('this is a todo test and is not treated as a failure');
  throw new Error('this does not fail the test');
}); 

describe()it()のエイリアス#

スイートとテストは、describe()it() 関数を使用して記述することもできます。describe()suite() のエイリアスであり、it()test() のエイリアスです。

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);
    });
  });
}); 

describe()it()node:test モジュールからインポートされます。

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

onlyテスト#

Node.jsが --test-only コマンドラインオプションで起動された場合、またはテストの分離が無効になっている場合、実行すべきテストに only オプションを渡すことで、選択されたサブセットを除くすべてのテストをスキップすることが可能です。only オプションが設定されたテストがある場合、すべてのサブテストも実行されます。スイートに only オプションが設定されている場合、そのスイート内のすべてテストが実行されます。ただし、only オプションが設定された子孫がある場合は、それらのテストのみが実行されます。

test()/it() 内で サブテスト を使用する場合、テストの選択されたサブセットのみを実行するには、すべての祖先テストに only オプションを付ける必要があります。

テストコンテキストの runOnly() メソッドを使用して、サブテストレベルで同じ動作を実装できます。実行されないテストは、テストランナーの出力から省略されます。

// Assume Node.js is run with the --test-only command-line option.
// The suite's 'only' option is set, so these tests are 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');
});

describe('a suite', () => {
  // The 'only' option is set, so this test is run.
  it('this test is run', { only: true }, () => {
    // This code is run.
  });

  it('this test is not run', () => {
    // This code is not run.
    throw new Error('fail');
  });
});

describe.only('a suite', () => {
  // The 'only' option is set, so this test is run.
  it('this test is run', () => {
    // This code is run.
  });

  it('this test is run', () => {
    // This code is run.
  });
}); 

名前によるテストのフィルタリング#

--test-name-pattern コマンドラインオプションを使用すると、名前が指定されたパターンに一致するテストのみを実行でき、--test-skip-pattern オプションを使用すると、名前が指定されたパターンに一致するテストをスキップできます。テスト名のパターンはJavaScriptの正規表現として解釈されます。--test-name-pattern--test-skip-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"(または --test-skip-pattern="/test [4-5]/i")でNode.jsを起動すると、パターンが大文字と小文字を区別しないため、Test 4Test 5 に一致します。

パターンで単一のテストを一致させるには、一意性を確保するために、すべての祖先テスト名をスペースで区切って接頭辞として付けることができます。たとえば、次のテストファイルがあるとします。

describe('test 1', (t) => {
  it('some test');
});

describe('test 2', (t) => {
  it('some test');
}); 

--test-name-pattern="test 1 some test" でNode.jsを起動すると、test 1 内の some test のみに一致します。

テスト名のパターンは、テストランナーが実行するファイルのセットを変更しません。

--test-name-pattern--test-skip-pattern の両方が指定された場合、テストは実行されるために 両方 の要件を満たす必要があります。

無関係な非同期アクティビティ#

テスト関数の実行が終了すると、テストの順序を維持しながら、結果はできるだけ早く報告されます。ただし、テスト関数がテスト自体よりも長く続く非同期アクティビティを生成する可能性があります。テストランナーはこの種のアクティビティを処理しますが、それに対応するためにテスト結果の報告を遅らせることはありません。

次の例では、2つの setImmediate() 操作が未完了のままテストが完了します。最初の setImmediate() は新しいサブテストを作成しようとします。親テストはすでに終了して結果を出力しているため、新しいサブテストはすぐに失敗としてマークされ、後で <TestsStream> に報告されます。

2番目の setImmediate()uncaughtException イベントを作成します。完了したテストから発生する uncaughtException および unhandledRejection イベントは、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 - Experimental

Node.jsテストランナーは、--watch フラグを渡すことでウォッチモードでの実行をサポートします。

node --test --watch 

ウォッチモードでは、テストランナーはテストファイルとその依存関係の変更を監視します。変更が検出されると、テストランナーは変更の影響を受けたテストを再実行します。テストランナーは、プロセスが終了するまで実行を続けます。

グローバルなセットアップとティアダウン#

安定性: 1.0 - Early development

テストランナーは、すべてのテストが実行される前に評価されるモジュールを指定することをサポートしており、テストのグローバルな状態やフィクスチャをセットアップするために使用できます。これは、複数のテストで必要とされるリソースを準備したり、共有状態をセットアップしたりするのに便利です。

このモジュールは、次のいずれかをエクスポートできます。

  • すべてのテストが開始される前に一度実行される globalSetup 関数
  • すべてのテストが完了した後に一度実行される globalTeardown 関数

このモジュールは、コマンドラインからテストを実行する際に --test-global-setup フラグを使用して指定します。

// setup-module.js
async function globalSetup() {
  // Setup shared resources, state, or environment
  console.log('Global setup executed');
  // Run servers, create files, prepare databases, etc.
}

async function globalTeardown() {
  // Clean up resources, state, or environment
  console.log('Global teardown executed');
  // Close servers, remove files, disconnect from databases, etc.
}

module.exports = { globalSetup, globalTeardown };// setup-module.mjs
export async function globalSetup() {
  // Setup shared resources, state, or environment
  console.log('Global setup executed');
  // Run servers, create files, prepare databases, etc.
}

export async function globalTeardown() {
  // Clean up resources, state, or environment
  console.log('Global teardown executed');
  // Close servers, remove files, disconnect from databases, etc.
}

グローバルセットアップ関数がエラーをスローした場合、テストは実行されず、プロセスはゼロ以外の終了コードで終了します。この場合、グローバルティアダウン関数は呼び出されません。

コマンドラインからのテスト実行#

Node.jsテストランナーは、--test フラグを渡すことでコマンドラインから呼び出すことができます。

node --test 

デフォルトでは、Node.jsはこれらのパターンに一致するすべてのファイルを実行します。

  • **/*.test.{cjs,mjs,js}
  • **/*-test.{cjs,mjs,js}
  • **/*_test.{cjs,mjs,js}
  • **/test-*.{cjs,mjs,js}
  • **/test.{cjs,mjs,js}
  • **/test/**/*.{cjs,mjs,js}

--no-experimental-strip-types が指定されない限り、次の追加パターンも一致します。

  • **/*.test.{cts,mts,ts}
  • **/*-test.{cts,mts,ts}
  • **/*_test.{cts,mts,ts}
  • **/test-*.{cts,mts,ts}
  • **/test.{cts,mts,ts}
  • **/test/**/*.{cts,mts,ts}

または、以下に示すように、1つ以上のグロブパターンをNode.jsコマンドの最後の引数として提供することもできます。グロブパターンは glob(7) の動作に従います。グロブパターンは、シェルによる展開を防ぐためにコマンドラインで二重引用符で囲む必要があります。これにより、システム間での移植性が低下する可能性があります。

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

一致するファイルはテストファイルとして実行されます。テストファイルの実行に関する詳細は、テストランナーの実行モデルのセクションで確認できます。

テストランナーの実行モデル#

プロセスレベルのテスト分離が有効な場合、一致する各テストファイルは別々の子プロセスで実行されます。同時に実行される子プロセスの最大数は、--test-concurrency フラグによって制御されます。子プロセスが終了コード0で終了した場合、テストは成功と見なされます。それ以外の場合、テストは失敗と見なされます。テストファイルはNode.jsで実行可能である必要がありますが、内部で node:test モジュールを使用する必要はありません。

各テストファイルは、通常のスクリプトであるかのように実行されます。つまり、テストファイル自体が node:test を使用してテストを定義している場合、それらのすべてのテストは、test()concurrency オプションの値に関係なく、単一のアプリケーショスレッド内で実行されます。

プロセスレベルのテスト分離が無効になっている場合、一致する各テストファイルはテストランナープロセスにインポートされます。すべてのテストファイルがロードされると、トップレベルのテストは並行性1で実行されます。すべてのテストファイルが同じコンテキスト内で実行されるため、分離が有効な場合には不可能な方法でテストが相互作用する可能性があります。たとえば、テストがグローバルな状態に依存している場合、その状態が別のファイルから来たテストによって変更される可能性があります。

子プロセスのオプション継承#

プロセス分離モード(デフォルト)でテストを実行する場合、生成された子プロセスは、設定ファイルで指定されたものを含む、親プロセスからNode.jsオプションを継承します。ただし、テストランナーの適切な機能を有効にするために、特定のフラグは除外されます。

  • --test - 再帰的なテスト実行を避けるために防止
  • --experimental-test-coverage - テストランナーによって管理
  • --watch - ウォッチモードは親レベルで処理
  • --experimental-default-config-file - 設定ファイルの読み込みは親によって処理
  • --test-reporter - レポートは親プロセスによって管理
  • --test-reporter-destination - 出力先は親によって制御
  • --experimental-config-file - 設定ファイルパスは親によって管理

コマンドライン引数、環境変数、および設定ファイルからの他のすべてのNode.jsオプションは、子プロセスに継承されます。

コードカバレッジの収集#

安定性: 1 - Experimental

Node.jsが --experimental-test-coverage コマンドラインフラグで起動された場合、すべてのテストが完了した後にコードカバレッジが収集され、統計が報告されます。NODE_V8_COVERAGE 環境変数を使用してコードカバレッジディレクトリを指定した場合、生成されたV8カバレッジファイルはそのディレクトリに書き込まれます。Node.jsコアモジュールおよび node_modules/ ディレクトリ内のファイルは、デフォルトではカバレッジレポートに含まれません。ただし、--test-coverage-include フラグを介して明示的に含めることができます。デフォルトでは、一致するすべてのテストファイルはカバレッジレポートから除外されます。除外は --test-coverage-exclude フラグを使用して上書きできます。カバレッジが有効な場合、カバレッジレポートは '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 */ 

カバレッジは、指定された行数に対しても無効にすることができます。指定された行数の後、カバレッジは自動的に再度有効になります。行数が明示的に提供されていない場合、1行が無視されます。

/* 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:test モジュールは、トップレベルの mock オブジェクトを介してテスト中のモックをサポートします。次の例では、2つの数値を加算する関数にスパイを作成します。スパイは、関数が期待どおりに呼び出されたことをアサートするために使用されます。

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.callCount(), 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.callCount(), 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.callCount(), 0);
  assert.strictEqual(sum(3, 4), 7);
  assert.strictEqual(sum.mock.callCount(), 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.callCount(), 0);
  assert.strictEqual(number.add(3), 8);
  assert.strictEqual(number.add.mock.callCount(), 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:timers および node:timers/promises モジュール、ならびにNode.jsのグローバルコンテキストの setTimeout 関数がモックされます。

注: import { setTimeout } from 'node:timers' のような関数の分割代入は、現在このAPIではサポートされていません。

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 will 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です。.enable() メソッドに now プロパティを渡すことで、初期の日付を設定できます。この値は、モックされた 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);
});

スナップショットテスト#

スナップショットテストでは、任意の値を文字列値にシリアル化し、既知の正しい値のセットと比較することができます。既知の正しい値はスナップショットとして知られ、スナップショットファイルに保存されます。スナップショットファイルはテストランナーによって管理されますが、デバッグを支援するために人間が読めるように設計されています。ベストプラクティスは、スナップショットファイルをテストファイルと一緒にソース管理にチェックインすることです。

スナップショットファイルは、--test-update-snapshots コマンドラインフラグでNode.jsを起動することによって生成されます。テストファイルごとに個別のスナップショットファイルが生成されます。デフォルトでは、スナップショットファイルはテストファイルと同じ名前に .snapshot ファイル拡張子が付いたものになります。この動作は snapshot.setResolveSnapshotPath() 関数を使用して設定できます。各スナップショットアサーションは、スナップショットファイル内のエクスポートに対応します。

以下にスナップショットテストの例を示します。このテストが初めて実行されるとき、対応するスナップショットファイルが存在しないため失敗します。

// test.js
suite('suite of snapshot tests', () => {
  test('snapshot test', (t) => {
    t.assert.snapshot({ value1: 1, value2: 2 });
    t.assert.snapshot(5);
  });
}); 

--test-update-snapshots を付けてテストファイルを実行して、スナップショットファイルを生成します。テストは成功し、テストファイルと同じディレクトリに test.js.snapshot という名前のファイルが作成されます。スナップショットファイルの内容を以下に示します。各スナップショットは、テストのフルネームと、同じテスト内のスナップショットを区別するためのカウンターによって識別されます。

exports[`suite of snapshot tests > snapshot test 1`] = `
{
  "value1": 1,
  "value2": 2
}
`;

exports[`suite of snapshot tests > snapshot test 2`] = `
5
`; 

スナップショットファイルが作成されたら、--test-update-snapshots フラグなしで再度テストを実行します。これでテストは成功するはずです。

テストレポーター#

node:test モジュールは、テストランナーが特定のレポーターを使用するために --test-reporter フラグを渡すことをサポートしています。

以下の組み込みレポーターがサポートされています。

  • spec spec レポーターは、テスト結果を人間が読みやすい形式で出力します。これがデフォルトのレポーターです。

  • tap tap レポーターは、テスト結果を TAP 形式で出力します。

  • dot dot レポーターは、テスト結果をコンパクトな形式で出力し、成功した各テストは . で、失敗した各テストは X で表されます。

  • junit junitレポーターはテスト結果をjUnit XML形式で出力します。

  • lcov lcov レポーターは、--experimental-test-coverage フラグとともに使用された場合、テストカバレッジを出力します。

これらのレポーターの正確な出力は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:watch:restarted':
        callback(null, 'test watch restarted due to file change');
        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:watch:restarted':
        callback(null, 'test watch restarted due to file change');
        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\n`;
        break;
      case 'test:enqueue':
        yield `test ${event.data.name} enqueued\n`;
        break;
      case 'test:watch:drained':
        yield 'test watch queue drained\n';
        break;
      case 'test:watch:restarted':
        yield 'test watch restarted due to file change\n';
        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;
      }
    }
  }
}module.exports = async function * customReporter(source) {
  for await (const event of source) {
    switch (event.type) {
      case 'test:dequeue':
        yield `test ${event.data.name} dequeued\n`;
        break;
      case 'test:enqueue':
        yield `test ${event.data.name} enqueued\n`;
        break;
      case 'test:watch:drained':
        yield 'test watch queue drained\n';
        break;
      case 'test:watch:restarted':
        yield 'test watch restarted due to file change\n';
        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> 数値が指定された場合、その数のテストプロセスが並列で実行されます。各プロセスは1つのテストファイルに対応します。true の場合、os.availableParallelism() - 1 個のテストファイルが並列で実行されます。false の場合、一度に1つのテストファイルのみが実行されます。デフォルト: false
    • cwd <string> テストランナーが使用する現在の作業ディレクトリを指定します。そのディレクトリからコマンドラインからテストを実行するかのように、ファイルを解決するためのベースパスとして機能します。デフォルト: process.cwd()
    • files <Array> 実行するファイルのリストを含む配列。デフォルト: コマンドラインからのテスト実行と同じです。
    • forceExit <boolean> イベントループがアクティブなままであっても、すべての既知のテストの実行が完了したらプロセスを終了するようにテストランナーを設定します。デフォルト: false
    • globPatterns <Array> テストファイルをマッチさせるためのグロブパターンのリストを含む配列。このオプションは files と一緒に使用することはできません。デフォルト: コマンドラインからのテスト実行と同じです。
    • inspectPort <number> | <Function> テスト子プロセスのインスペクターポートを設定します。これは数値、または引数を取らずに数値を返す関数にすることができます。nullishな値が指定された場合、各プロセスはプライマリの process.debugPort からインクリメントされた独自のポートを取得します。isolation オプションが 'none' に設定されている場合、子プロセスは生成されないため、このオプションは無視されます。デフォルト: undefined
    • isolation <string> テストの分離タイプを設定します。'process' に設定すると、各テストファイルは別々の子プロセスで実行されます。'none' に設定すると、すべてのテストファイルが現在のプロセスで実行されます。デフォルト: 'process'
    • only <boolean> truthyな場合、テストコンテキストは only オプションが設定されているテストのみを実行します。
    • setup <Function> TestsStream インスタンスを受け取り、テストが実行される前にリスナーをセットアップするために使用できる関数。デフォルト: undefined
    • execArgv <Array> サブプロセスを生成する際に node 実行可能ファイルに渡すCLIフラグの配列。このオプションは isolation'none' の場合は効果がありません。デフォルト: []
    • argv <Array> サブプロセスを生成する際に各テストファイルに渡すCLIフラグの配列。このオプションは isolation'none' の場合は効果がありません。デフォルト: []
    • signal <AbortSignal> 進行中のテスト実行を中止できるようにします。
    • testNamePatterns <string> | <RegExp> | <Array> 文字列、RegExp、またはRegExpの配列。名前が指定されたパターンに一致するテストのみを実行するために使用できます。テスト名のパターンはJavaScriptの正規表現として解釈されます。実行される各テストについて、beforeEach() のような対応するテストフックも実行されます。デフォルト: undefined
    • testSkipPatterns <string> | <RegExp> | <Array> 文字列、RegExp、またはRegExpの配列。名前が指定されたパターンに一致するテストの実行を除外するために使用できます。テスト名のパターンはJavaScriptの正規表現として解釈されます。実行される各テストについて、beforeEach() のような対応するテストフックも実行されます。デフォルト: undefined
    • timeout <number> テスト実行がこのミリ秒数を超えると失敗します。指定されていない場合、サブテストはこの値を親から継承します。デフォルト: Infinity
    • watch <boolean> ウォッチモードで実行するかどうか。デフォルト: false
    • shard <Object> 特定のシャードでテストを実行します。デフォルト: undefined
      • index <number> 実行するシャードのインデックスを指定する1から<total>までの正の整数です。このオプションは必須です。
      • total <number> テストファイルを分割するシャードの総数を指定する正の整数です。このオプションは必須です。
    • rerunFailuresFilePath <string> テストランナーがテストの状態を保存するファイルパス。次回の実行で失敗したテストのみを再実行できるようにします。詳細は[失敗したテストの再実行][]を参照してください。デフォルト: undefined
    • coverage <boolean> コードカバレッジの収集を有効にします。デフォルト: false
    • coverageExcludeGlobs <string> | <Array> グロブパターンを使用して特定のファイルをコードカバレッジから除外します。このパターンは絶対パスと相対パスの両方にマッチできます。このプロパティは、coveragetrue に設定されている場合にのみ適用されます。coverageExcludeGlobscoverageIncludeGlobs の両方が提供された場合、ファイルはカバレッジレポートに含まれるために 両方 の基準を満たす必要があります。デフォルト: undefined
    • coverageIncludeGlobs <string> | <Array> グロブパターンを使用して特定のファイルをコードカバレッジに含めます。このパターンは絶対パスと相対パスの両方にマッチできます。このプロパティは、coveragetrue に設定されている場合にのみ適用されます。coverageExcludeGlobscoverageIncludeGlobs の両方が提供された場合、ファイルはカバレッジレポートに含まれるために 両方 の基準を満たす必要があります。デフォルト: undefined
    • lineCoverage <number> カバーされた行の最小パーセンテージを要求します。コードカバレッジが指定されたしきい値に達しない場合、プロセスはコード 1 で終了します。デフォルト: 0
    • branchCoverage <number> カバーされたブランチの最小パーセンテージを要求します。コードカバレッジが指定されたしきい値に達しない場合、プロセスはコード 1 で終了します。デフォルト: 0
    • functionCoverage <number> カバーされた関数の最小パーセンテージを要求します。コードカバレッジが指定されたしきい値に達しない場合、プロセスはコード 1 で終了します。デフォルト: 0
  • 戻り値: <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);

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

  • name <string> テスト結果を報告する際に表示されるスイートの名前。デフォルト: fnname プロパティ、または fn に名前がない場合は '<anonymous>'
  • options <Object> スイートのオプション設定。test([name][, options][, fn]) と同じオプションをサポートします。
  • fn <Function> | <AsyncFunction> ネストされたテストとスイートを宣言するスイート関数。この関数の最初の引数は SuiteContext オブジェクトです。デフォルト: 何もしない関数。
  • 戻り値: <Promise> undefined で即座に解決されます。

suite() 関数は node:test モジュールからインポートされます。

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

スイートをスキップするためのショートハンドです。これは suite([name], { skip: true }[, fn]) と同じです。

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

スイートを TODOとしてマークするためのショートハンドです。これは suite([name], { todo: true }[, fn]) と同じです。

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

スイートを only としてマークするためのショートハンドです。これは suite([name], { only: true }[, fn]) と同じです。

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

  • name <string> テスト結果を報告する際に表示されるテストの名前。デフォルト: fnname プロパティ、または fn に名前がない場合は '<anonymous>'
  • options <Object> テストの設定オプション。以下のプロパティがサポートされています。
    • concurrency <number> | <boolean> 数値が指定された場合、その数のテストがアプリケーショスレッド内で並列で実行されます。true の場合、スケジュールされたすべての非同期テストがスレッド内で同時に実行されます。false の場合、一度に1つのテストのみが実行されます。指定されていない場合、サブテストはこの値を親から継承します。デフォルト: false
    • only <boolean> truthyで、かつテストコンテキストが only テストを実行するように設定されている場合、このテストが実行されます。それ以外の場合、テストはスキップされます。デフォルト: false
    • signal <AbortSignal> 進行中のテストを中止できるようにします。
    • skip <boolean> | <string> truthyな場合、テストはスキップされます。文字列が指定された場合、その文字列はテスト結果でテストをスキップした理由として表示されます。デフォルト: false
    • todo <boolean> | <string> truthyな場合、テストは TODO としてマークされます。文字列が指定された場合、その文字列はテスト結果でテストが TODO である理由として表示されます。デフォルト: false
    • timeout <number> テストがこのミリ秒数を超えると失敗します。指定されていない場合、サブテストはこの値を親から継承します。デフォルト: Infinity
    • plan <number> テストで実行が期待されるアサーションとサブテストの数。テストで実行されたアサーションの数がプランで指定された数と一致しない場合、テストは失敗します。デフォルト: undefined
  • fn <Function> | <AsyncFunction> テスト対象の関数。この関数の最初の引数は TestContext オブジェクトです。テストがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 戻り値: <Promise> テストが完了すると undefined で解決されるか、テストがスイート内で実行される場合は即座に解決されます。

test() 関数は test モジュールからインポートされる値です。この関数を呼び出すたびに、テストが <TestsStream> に報告されます。

fn 引数に渡される TestContext オブジェクトは、現在のテストに関連するアクションを実行するために使用できます。例としては、テストのスキップ、追加の診断情報の追加、サブテストの作成などがあります。

test() は、テストが完了すると解決する Promise を返します。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])#

suite() のエイリアスです。

describe() 関数は node:test モジュールからインポートされます。

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> フック関数。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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> フック関数。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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> フック関数。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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> フック関数。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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');
  });
}); 

assert#

現在のプロセスの TestContext オブジェクトで利用可能なアサーションを設定するために使用されるメソッドを持つオブジェクトです。node:assert のメソッドとスナップショットテスト関数はデフォルトで利用可能です。

--require または --import でプリロードされたモジュールに共通の設定コードを配置することで、すべてのファイルに同じ設定を適用することが可能です。

assert.register(name, fn)#

指定された名前と関数で新しいアサーション関数を定義します。同じ名前のアサーションが既に存在する場合、上書きされます。

snapshot#

現在のプロセスにおけるデフォルトのスナップショット設定を構成するために使用されるメソッドを持つオブジェクトです。--require または --import でプリロードされたモジュールに共通の設定コードを配置することで、すべてのファイルに同じ設定を適用することが可能です。

snapshot.setDefaultSnapshotSerializers(serializers)#

  • serializers <Array> スナップショットテストのデフォルトシリアライザとして使用される同期関数の配列。

この関数は、テストランナーが使用するデフォルトのシリアル化メカニズムをカスタマイズするために使用されます。デフォルトでは、テストランナーは提供された値に対して JSON.stringify(value, null, 2) を呼び出すことでシリアル化を実行します。JSON.stringify() には、循環構造やサポートされるデータ型に関する制限があります。より堅牢なシリアル化メカニズムが必要な場合は、この関数を使用する必要があります。

snapshot.setResolveSnapshotPath(fn)#

  • fn <Function> スナップショットファイルの場所を計算するために使用される関数。この関数は、テストファイルのパスを唯一の引数として受け取ります。テストがファイルに関連付けられていない場合(例えばREPL内)、入力は undefined です。fn() は、スナップショットファイルのスナップショットの場所を指定する文字列を返す必要があります。

この関数は、スナップショットテストに使用されるスナップショットファイルの場所をカスタマイズするために使用されます。デフォルトでは、スナップショットファイル名はエントリポイントファイル名と同じで、.snapshot ファイル拡張子が付いています。

クラス: MockFunctionContext#

MockFunctionContext クラスは、MockTracker APIを介して作成されたモックの動作を検査または操作するために使用されます。

ctx.calls#

モックへの呼び出しを追跡するために使用される内部配列のコピーを返すゲッターです。配列の各エントリは、次のプロパティを持つオブジェクトです。

  • arguments <Array> モック関数に渡された引数の配列。
  • error <any> モックされた関数がスローした場合、このプロパティにはスローされた値が含まれます。デフォルト: undefined
  • result <any> モックされた関数によって返された値。
  • stack <Error> そのスタックを使用してモックされた関数の呼び出し元を特定できる Error オブジェクト。
  • target <Function> | <undefined> モックされた関数がコンストラクタの場合、このフィールドには構築されているクラスが含まれます。それ以外の場合、これは undefined になります。
  • this <any> モックされた関数の this の値。

ctx.callCount()#

  • 戻り値: <integer> このモックが呼び出された回数。

この関数は、このモックが呼び出された回数を返します。この関数は、ctx.calls.length をチェックするよりも効率的です。なぜなら、ctx.calls は内部の呼び出し追跡配列のコピーを作成するゲッターだからです。

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()#

モック関数の実装を元の動作にリセットします。この関数を呼び出した後もモックは使用できます。

クラス: MockModuleContext#

安定性: 1.0 - Early development

MockModuleContext クラスは、MockTracker API を介して作成されたモジュールモックの動作を操作するために使用されます。

ctx.restore()#

モックモジュールの実装をリセットします。

クラス: MockPropertyContext#

MockPropertyContext クラスは、MockTracker API を介して作成されたプロパティモックの動作を検査または操作するために使用されます。

ctx.accesses#

モックされたプロパティへのアクセス(get/set)を追跡するために使用される内部配列のコピーを返すゲッターです。配列の各エントリは、次のプロパティを持つオブジェクトです。

  • type <string> 'get' または 'set' のいずれかで、アクセスのタイプを示します。
  • value <any> 読み取られた('get' の場合)または書き込まれた('set' の場合)値。
  • stack <Error> そのスタックを使用してモックされた関数の呼び出し元を特定できる Error オブジェクト。

ctx.accessCount()#

  • 戻り値: <integer> プロパティがアクセスされた(読み取りまたは書き込みされた)回数。

この関数は、プロパティがアクセスされた回数を返します。この関数は、ctx.accesses.length をチェックするよりも効率的です。なぜなら、ctx.accesses は内部のアクセス追跡配列のコピーを作成するゲッターだからです。

ctx.mockImplementation(value)#

  • value <any> モックされたプロパティ値として設定される新しい値。

この関数は、モックされたプロパティゲッターによって返される値を変更するために使用されます。

ctx.mockImplementationOnce(value[, onAccess])#

  • value <any> onAccess で指定された呼び出し番号に対してモックの実装として使用される値。
  • onAccess <integer> value を使用する呼び出し番号。指定された呼び出しが既に発生している場合、例外がスローされます。デフォルト: 次の呼び出しの番号。

この関数は、単一の呼び出しに対して既存のモックの動作を変更するために使用されます。呼び出し onAccess が発生すると、モックは mockImplementationOnce() が呼び出されなかった場合の動作に戻ります。

次の例では、t.mock.property() を使用してモック関数を作成し、モックプロパティを呼び出し、次の呼び出しのためにモックの実装を別の値に変更し、その後以前の動作を再開します。

test('changes a mock behavior once', (t) => {
  const obj = { foo: 1 };

  const prop = t.mock.property(obj, 'foo', 5);

  assert.strictEqual(obj.foo, 5);
  prop.mock.mockImplementationOnce(25);
  assert.strictEqual(obj.foo, 25);
  assert.strictEqual(obj.foo, 5);
}); 
注意点#

他のモックAPIとの一貫性のために、この関数はプロパティのgetとsetの両方をアクセスとして扱います。プロパティのsetが同じアクセスインデックスで発生した場合、「once」の値はset操作によって消費され、モックされたプロパティ値は「once」の値に変更されます。これにより、「once」の値がget操作でのみ使用されることを意図している場合、予期しない動作につながる可能性があります。

ctx.resetAccesses()#

モックされたプロパティのアクセス履歴をリセットします。

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` のインスタンスであり、モック関数の挙動を調査したり変更したりするために使用できます。

この関数はモック関数を作成するために使用されます。

次の例では、呼び出されるたびにカウンターを1つインクリメントするモック関数を作成します。`times` オプションを使用して、最初の2回の呼び出しではカウンターに1ではなく2を加えるようにモックの挙動を変更しています。

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])#

この関数は、`options.getter` を `true` に設定した `MockTracker.method` の糖衣構文です。

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> `true` の場合、`object[methodName]` はゲッターとして扱われます。このオプションは `setter` オプションと同時に使用することはできません。デフォルト: `false`。
    • setter <boolean> `true` の場合、`object[methodName]` はセッターとして扱われます。このオプションは `getter` オプションと同時に使用することはできません。デフォルト: `false`。
    • times <integer> モックが `implementation` の挙動を使用する回数。モックされたメソッドが `times` 回呼び出されると、自動的に元の挙動に復元されます。この値は0より大きい整数でなければなりません。デフォルト: `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.callCount(), 0);
  assert.strictEqual(number.subtract(3), 2);
  assert.strictEqual(number.subtract.mock.callCount(), 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.module(specifier[, options])#

安定性: 1.0 - Early development

  • specifier <string> | <URL> モックするモジュールを識別する文字列。
  • options <Object> モックモジュールのオプションの設定オプション。以下のプロパティがサポートされています。
    • cache <boolean> `false` の場合、`require()` または `import()` の各呼び出しで新しいモックモジュールが生成されます。`true` の場合、後続の呼び出しは同じモジュールモックを返し、モックモジュールは CommonJS キャッシュに挿入されます。デフォルト: `false`。
    • defaultExport <any> モックされたモジュールのデフォルトエクスポートとして使用されるオプションの値。この値が提供されない場合、ESM モックにはデフォルトエクスポートは含まれません。モックが CommonJS または組み込みモジュールの場合、この設定は `module.exports` の値として使用されます。この値が提供されない場合、CJS および組み込みモックは `module.exports` の値として空のオブジェクトを使用します。
    • namedExports <Object> モックモジュールの名前付きエクスポートを作成するためにキーと値が使用されるオプションのオブジェクト。モックが CommonJS または組み込みモジュールの場合、これらの値は `module.exports` にコピーされます。したがって、名前付きエクスポートと非オブジェクトのデフォルトエクスポートの両方でモックが作成された場合、CJS または組み込みモジュールとして使用されると例外をスローします。
  • 戻り値: <MockModuleContext> モックを操作するために使用できるオブジェクト。

この関数は、ECMAScript モジュール、CommonJS モジュール、JSON モジュール、および Node.js 組み込みモジュールのエクスポートをモックするために使用されます。モックする前の元のモジュールへの参照は影響を受けません。モジュールモックを有効にするには、Node.js を `--experimental-test-module-mocks` コマンドラインフラグで起動する必要があります。

次の例は、モジュールのモックがどのように作成されるかを示しています。

test('mocks a builtin module in both module systems', async (t) => {
  // Create a mock of 'node:readline' with a named export named 'fn', which
  // does not exist in the original 'node:readline' module.
  const mock = t.mock.module('node:readline', {
    namedExports: { fn() { return 42; } },
  });

  let esmImpl = await import('node:readline');
  let cjsImpl = require('node:readline');

  // cursorTo() is an export of the original 'node:readline' module.
  assert.strictEqual(esmImpl.cursorTo, undefined);
  assert.strictEqual(cjsImpl.cursorTo, undefined);
  assert.strictEqual(esmImpl.fn(), 42);
  assert.strictEqual(cjsImpl.fn(), 42);

  mock.restore();

  // The mock is restored, so the original builtin module is returned.
  esmImpl = await import('node:readline');
  cjsImpl = require('node:readline');

  assert.strictEqual(typeof esmImpl.cursorTo, 'function');
  assert.strictEqual(typeof cjsImpl.cursorTo, 'function');
  assert.strictEqual(esmImpl.fn, undefined);
  assert.strictEqual(cjsImpl.fn, undefined);
}); 

mock.property(object, propertyName[, value])#

  • object <Object> 値がモックされるオブジェクト。
  • propertyName <string> | <symbol> `object` 上のモックするプロパティの識別子。
  • value <any> `object[propertyName]` のモック値として使用されるオプションの値。デフォルト: 元のプロパティ値。
  • 戻り値: <Proxy> モックされたオブジェクトへのプロキシ。モックされたオブジェクトには特別な `mock` プロパティが含まれています。これは `MockPropertyContext` のインスタンスであり、モックされたプロパティの挙動を調査したり変更したりするために使用できます。

オブジェクトのプロパティ値のモックを作成します。これにより、特定のプロパティへのアクセスを追跡および制御できます。これには、読み取られた回数 (ゲッター) や書き込まれた回数 (セッター)、モック後の元の値への復元が含まれます。

test('mocks a property value', (t) => {
  const obj = { foo: 42 };
  const prop = t.mock.property(obj, 'foo', 100);

  assert.strictEqual(obj.foo, 100);
  assert.strictEqual(prop.mock.accessCount(), 1);
  assert.strictEqual(prop.mock.accesses[0].type, 'get');
  assert.strictEqual(prop.mock.accesses[0].value, 100);

  obj.foo = 200;
  assert.strictEqual(prop.mock.accessCount(), 2);
  assert.strictEqual(prop.mock.accesses[1].type, 'set');
  assert.strictEqual(prop.mock.accesses[1].value, 200);

  prop.mock.restore();
  assert.strictEqual(obj.foo, 42);
}); 

mock.reset()#

この関数は、この `MockTracker` によって以前に作成されたすべてのモックのデフォルトの挙動を復元し、モックと `MockTracker` インスタンスの関連付けを解除します。関連付けが解除された後もモックは使用できますが、`MockTracker` インスタンスはもはやそれらの挙動をリセットしたり、その他の方法で対話したりするために使用できなくなります。

各テストが完了した後、この関数はテストコンテキストの `MockTracker` で呼び出されます。グローバルな `MockTracker` が多用される場合は、この関数を手動で呼び出すことが推奨されます。

mock.restoreAll()#

この関数は、この `MockTracker` によって以前に作成されたすべてのモックのデフォルトの挙動を復元します。`mock.reset()` とは異なり、`mock.restoreAll()` はモックと `MockTracker` インスタンスの関連付けを解除しません。

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

この関数は、`options.setter` を `true` に設定した `MockTracker.method` の糖衣構文です。

クラス: `MockTimers`#

タイマーのモックは、ソフトウェアテストで一般的に使用される手法で、指定された時間間隔を実際に待つことなく、setIntervalsetTimeout などのタイマーの動作をシミュレートおよび制御します。

`MockTimers` は `Date` オブジェクトもモックできます。

`MockTracker` は、`MockTimers` インスタンスであるトップレベルの `timers` エクスポートを提供します。

timers.enable([enableOptions])#

指定されたタイマーのタイマーモックを有効にします。

  • enableOptions <Object> タイマーモックを有効にするためのオプションの設定オプション。以下のプロパティがサポートされています。
    • apis <Array> モックするタイマーを含むオプションの配列。現在サポートされているタイマー値は `'setInterval'`、`'setTimeout'`、`'setImmediate'`、および `'Date'` です。デフォルト: `['setInterval', 'setTimeout', 'setImmediate', 'Date']`。配列が提供されない場合、すべての時間関連 API (`'setInterval'`、`'clearInterval'`、`'setTimeout'`、`'clearTimeout'`、`'setImmediate'`、`'clearImmediate'`、および `'Date'`) がデフォルトでモックされます。
    • now <number> | <Date> `Date.now()` の値として使用する初期時刻 (ミリ秒単位) を表すオプションの数値または Date オブジェクト。デフォルト: `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/promises、および `globalThis` からの `setInterval` と `clearInterval` 関数のみがモックされます。

初期時刻を設定した使用例

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

初期 Date オブジェクトを時刻として設定した使用例

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'`、`'setImmediate'`、および `'clearImmediate'`) がモックされます。`node:timers`、`node:timers/promises`、および `globalThis` からの `setInterval`、`clearInterval`、`setTimeout`、`clearTimeout`、`setImmediate`、および `clearImmediate` 関数がモックされます。グローバルな `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> タイマーを進める時間 (ミリ秒単位)。デフォルト: `1`。

注: これは Node.js の `setTimeout` の挙動とは異なり、正の数のみを受け付けます。Node.js では、負の数を持つ `setTimeout` は Web 互換性の理由でのみサポートされています。

次の例では、`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 threeSeconds = 3000;
  context.mock.timers.tick(threeSeconds);
  context.mock.timers.tick(threeSeconds);
  context.mock.timers.tick(threeSeconds);

  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 threeSeconds = 3000;
  context.mock.timers.tick(threeSeconds);
  context.mock.timers.tick(threeSeconds);
  context.mock.timers.tick(threeSeconds);

  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);
});
クリア関数の使用#

前述の通り、タイマーからのすべてのクリア関数 (`clearTimeout`、`clearInterval`、`clearImmediate`) は暗黙的にモックされます。`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);

  // Implicitly 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);

  // Implicitly 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 グローバルコンテキストからのタイマーが有効になります。

注: import { setTimeout } from 'node:timers' のような関数の分割代入は、現在このAPIではサポートされていません。

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 からの `setInterval` は `AsyncGenerator` であり、この 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)#

モックされた `Date` オブジェクトの参照として使用される現在の Unix タイムスタンプを設定します。

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` オブジェクトに渡した場合、`setTimeout` と `setInterval` で設定されたタイマーは影響を受けません

ただし、`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 <Array> 個々のファイルのカバレッジレポートの配列。各レポートは以下のスキーマを持つオブジェクトです。
        • path <string> ファイルの絶対パス。
        • totalLineCount <number> 総行数。
        • totalBranchCount <number> 総ブランチ数。
        • totalFunctionCount <number> 総関数数。
        • coveredLineCount <number> カバーされた行数。
        • coveredBranchCount <number> カバーされたブランチ数。
        • coveredFunctionCount <number> カバーされた関数数。
        • coveredLinePercent <number> カバーされた行の割合。
        • coveredBranchPercent <number> カバーされたブランチの割合。
        • coveredFunctionPercent <number> カバーされた関数の割合。
        • functions <Array> 関数カバレッジを表す関数の配列。
          • name <string> 関数の名前。
          • line <number> 関数が定義されている行番号。
          • count <number> 関数が呼び出された回数。
        • branches <Array> ブランチカバレッジを表すブランチの配列。
          • line <number> ブランチが定義されている行番号。
          • count <number> ブランチが通過した回数。
        • lines <Array> 行番号とそれがカバーされた回数を表す行の配列。
          • line <number> 行番号。
          • count <number> その行がカバーされた回数。
      • thresholds <Object> 各カバレッジタイプのカバレッジが閾値を満たしているかどうかを含むオブジェクト。
        • function <number> 関数カバレッジの閾値。
        • branch <number> ブランチカバレッジの閾値。
        • line <number> 行カバレッジの閾値。
      • totals <Object> 全ファイルのカバレッジの要約を含むオブジェクト。
        • totalLineCount <number> 総行数。
        • totalBranchCount <number> 総ブランチ数。
        • totalFunctionCount <number> 総関数数。
        • coveredLineCount <number> カバーされた行数。
        • coveredBranchCount <number> カバーされたブランチ数。
        • coveredFunctionCount <number> カバーされた関数数。
        • coveredLinePercent <number> カバーされた行の割合。
        • coveredBranchPercent <number> カバーされたブランチの割合。
        • coveredFunctionPercent <number> カバーされた関数の割合。
      • workingDirectory <string> コードカバレッジが開始されたときの作業ディレクトリ。テストが Node.js プロセスの作業ディレクトリを変更した場合に、相対パス名を表示するのに便利です。
    • nesting <number> テストのネストレベル。

コードカバレッジが有効で、すべてのテストが完了したときに発行されます。

イベント: `'test:complete'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • details <Object> 追加の実行メタデータ。
      • passed <boolean> テストが合格したかどうか。
      • duration_ms <number> テストの実行時間 (ミリ秒)。
      • error <Error> | <undefined> テストが合格しなかった場合にスローされたエラーをラップするエラー。
        • cause <Error> テストによって実際にスローされたエラー。
      • type <string> | <undefined> テストのタイプ。これがスイートであるかどうかを示すために使用されます。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • name <string> テスト名。
    • nesting <number> テストのネストレベル。
    • testNumber <number> テストの序数。
    • todo <string> | <boolean> | <undefined> `context.todo` が呼び出された場合に存在します。
    • skip <string> | <boolean> | <undefined> `context.skip` が呼び出された場合に存在します。

テストが実行を完了したときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されるわけではありません。対応する宣言順のイベントは `'test:pass'` と `'test:fail'` です。

イベント: `'test:dequeue'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • name <string> テスト名。
    • nesting <number> テストのネストレベル。
    • type <string> テストのタイプ。`'suite'` または `'test'` のいずれかです。

テストがデキューされ、実行される直前に発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることは保証されていません。対応する宣言順のイベントは `'test:start'` です。

イベント: `'test:diagnostic'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • message <string> 診断メッセージ。
    • nesting <number> テストのネストレベル。
    • level <string> 診断メッセージの重要度レベル。可能な値は以下の通りです。
      • 'info': 情報メッセージ。
      • 'warn': 警告。
      • 'error': エラー。

`context.diagnostic` が呼び出されたときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることが保証されています。

イベント: `'test:enqueue'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • name <string> テスト名。
    • nesting <number> テストのネストレベル。
    • type <string> テストのタイプ。`'suite'` または `'test'` のいずれかです。

テストが実行のためにエンキューされたときに発行されます。

イベント: `'test:fail'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • details <Object> 追加の実行メタデータ。
      • duration_ms <number> テストの実行時間 (ミリ秒)。
      • error <Error> テストによってスローされたエラーをラップするエラー。
        • cause <Error> テストによって実際にスローされたエラー。
      • type <string> | <undefined> テストのタイプ。これがスイートであるかどうかを示すために使用されます。
      • attempt <number> | <undefined> テスト実行の試行回数。`--test-rerun-failures` フラグを使用している場合にのみ存在します。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • name <string> テスト名。
    • nesting <number> テストのネストレベル。
    • testNumber <number> テストの序数。
    • todo <string> | <boolean> | <undefined> `context.todo` が呼び出された場合に存在します。
    • skip <string> | <boolean> | <undefined> `context.skip` が呼び出された場合に存在します。

テストが失敗したときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることが保証されています。対応する実行順のイベントは `'test:complete'` です。

イベント: `'test:pass'`#

テストが合格したときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることが保証されています。対応する実行順のイベントは `'test:complete'` です。

イベント: `'test:plan'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • nesting <number> テストのネストレベル。
    • count <number> 実行されたサブテストの数。

特定のテストのすべてのサブテストが完了したときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることが保証されています。

イベント: `'test:start'`#

  • data <Object>
    • column <number> | <undefined> テストが定義されている列番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • file <string> | <undefined> テストファイルのパス、テストが REPL を通じて実行された場合は `undefined`。
    • line <number> | <undefined> テストが定義されている行番号、またはテストが REPL を通じて実行された場合は `undefined`。
    • name <string> テスト名。
    • nesting <number> テストのネストレベル。

テストが自身およびそのサブテストのステータス報告を開始したときに発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることが保証されています。対応する実行順のイベントは `'test:dequeue'` です。

イベント: `'test:stderr'`#

  • data <Object>
    • file <string> テストファイルのパス。
    • message <string> `stderr` に書き込まれたメッセージ。

実行中のテストが `stderr` に書き込んだときに発行されます。このイベントは `--test` フラグが渡された場合にのみ発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることは保証されていません。

イベント: `'test:stdout'`#

  • data <Object>
    • file <string> テストファイルのパス。
    • message <string> `stdout` に書き込まれたメッセージ。

実行中のテストが `stdout` に書き込んだときに発行されます。このイベントは `--test` フラグが渡された場合にのみ発行されます。このイベントは、テストが定義された順序と同じ順序で発行されることは保証されていません。

イベント: `'test:summary'`#

  • data <Object>
    • counts <Object> 様々なテスト結果のカウントを含むオブジェクト。
      • cancelled <number> キャンセルされたテストの総数。
      • failed <number> 失敗したテストの総数。
      • passed <number> 合格したテストの総数。
      • skipped <number> スキップされたテストの総数。
      • suites <number> 実行されたスイートの総数。
      • tests <number> スイートを除いた、実行されたテストの総数。
      • todo <number> TODO テストの総数。
      • topLevel <number> トップレベルのテストとスイートの総数。
    • duration_ms <number> テスト実行の持続時間 (ミリ秒)。
    • file <string> | <undefined> サマリーを生成したテストファイルのパス。サマリーが複数のファイルに対応する場合、この値は `undefined` です。
    • success <boolean> テスト実行が成功と見なされるかどうかを示します。失敗したテストや満たされないカバレッジ閾値などのエラー条件が発生した場合、この値は `false` に設定されます。

テスト実行が完了したときに発行されます。このイベントには完了したテスト実行に関するメトリクスが含まれ、テスト実行が成功したか失敗したかを判断するのに役立ちます。プロセスレベルのテスト分離が使用されている場合、最終的な累積サマリーに加えて、各テストファイルに対して `'test:summary'` イベントが生成されます。

イベント: `'test:watch:drained'`#

ウォッチモードで実行キューにテストがなくなったときに発行されます。

イベント: `'test:watch:restarted'`#

ウォッチモードでファイルの変更により1つ以上のテストが再開されたときに発行されます。

クラス: `TestContext`#

`TestContext` のインスタンスは、テストランナーと対話するために各テスト関数に渡されます。ただし、`TestContext` コンストラクタは API の一部として公開されていません。

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

  • fn <Function> | <AsyncFunction> フック関数。この関数の最初の引数は `TestContext` オブジェクトです。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • options <Object> フックの設定オプション。以下のプロパティがサポートされています。
    • signal <AbortSignal> 進行中のフックを中止できるようにします。
    • timeout <number> フックがこのミリ秒数を超えると失敗します。指定されていない場合、サブテストはこの値を親から継承します。デフォルト: Infinity

この関数は、現在のテストのサブテストの前に実行されるフックを作成するために使用されます。

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

  • fn <Function> | <AsyncFunction> フック関数。この関数の最初の引数は `TestContext` オブジェクトです。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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` オブジェクトです。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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` オブジェクトです。フックがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 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.assert#

`context` にバインドされたアサーションメソッドを含むオブジェクト。`node:assert` モジュールのトップレベル関数が、テストプランを作成する目的でここで公開されています。

test('test', (t) => {
  t.plan(1);
  t.assert.strictEqual(true, true);
}); 
context.assert.fileSnapshot(value, path[, options])#
  • value <any> 文字列にシリアライズする値。Node.js が `--test-update-snapshots` フラグで起動された場合、シリアライズされた値は `path` に書き込まれます。それ以外の場合、シリアライズされた値は既存のスナップショットファイルの内容と比較されます。
  • path <string> シリアライズされた `value` が書き込まれるファイル。
  • options <Object> オプションの設定オプション。以下のプロパティがサポートされています。
    • serializers <Array> `value` を文字列にシリアライズするために使用される同期関数の配列。`value` は最初のシリアライザ関数の唯一の引数として渡されます。各シリアライザの戻り値は、次のシリアライザへの入力として渡されます。すべてのシリアライザが実行されると、結果の値は文字列に強制変換されます。デフォルト: シリアライザが提供されない場合、テストランナーのデフォルトのシリアライザが使用されます。

この関数は `value` をシリアライズし、`path` で指定されたファイルに書き込みます。

test('snapshot test with default serialization', (t) => {
  t.assert.fileSnapshot({ value1: 1, value2: 2 }, './snapshots/snapshot.json');
}); 

この関数は `context.assert.snapshot()` と以下の点で異なります。

  • スナップショットファイルのパスはユーザーによって明示的に提供されます。
  • 各スナップショットファイルは単一のスナップショット値に制限されます。
  • テストランナーによる追加のエスケープは行われません。

これらの違いにより、スナップショットファイルはシンタックスハイライトなどの機能をより良くサポートできます。

context.assert.snapshot(value[, options])#
  • value <any> 文字列にシリアライズする値。Node.js が `--test-update-snapshots` フラグで起動された場合、シリアライズされた値はスナップショットファイルに書き込まれます。それ以外の場合、シリアライズされた値は既存のスナップショットファイル内の対応する値と比較されます。
  • options <Object> オプションの設定オプション。以下のプロパティがサポートされています。
    • serializers <Array> `value` を文字列にシリアライズするために使用される同期関数の配列。`value` は最初のシリアライザ関数の唯一の引数として渡されます。各シリアライザの戻り値は、次のシリアライザへの入力として渡されます。すべてのシリアライザが実行されると、結果の値は文字列に強制変換されます。デフォルト: シリアライザが提供されない場合、テストランナーのデフォルトのシリアライザが使用されます。

この関数は、スナップショットテストのためのアサーションを実装します。

test('snapshot test with default serialization', (t) => {
  t.assert.snapshot({ value1: 1, value2: 2 });
});

test('snapshot test with custom serialization', (t) => {
  t.assert.snapshot({ value3: 3, value4: 4 }, {
    serializers: [(value) => JSON.stringify(value)],
  });
}); 

context.diagnostic(message)#

  • message <string> 報告されるメッセージ。

この関数は、出力に診断情報を書き込むために使用されます。診断情報はテスト結果の最後に含まれます。この関数は値を返しません。

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

context.filePath#

現在のテストを作成したテストファイルの絶対パス。テストファイルがテストを生成する追加のモジュールをインポートする場合、インポートされたテストはルートテストファイルのパスを返します。

context.fullName#

テストとその各祖先の名前が `>` で区切られたもの。

context.name#

テストの名前。

context.plan(count[,options])#

  • count <number> 実行が期待されるアサーションとサブテストの数。
  • options <Object> プランの追加オプション。
    • wait <boolean> | <number> プランの待機時間。
      • `true` の場合、プランはすべてのアサーションとサブテストが実行されるのを無期限に待ちます。
      • `false` の場合、プランはテスト関数が完了した直後に即時チェックを行い、保留中のアサーションやサブテストを待ちません。このチェック後に完了したアサーションやサブテストはプランにカウントされません。
      • 数値の場合、期待されるアサーションとサブテストが一致するのを待つ間の最大待機時間をミリ秒で指定します。タイムアウトに達した場合、テストは失敗します。デフォルト: `false`。

この関数は、テスト内で実行が期待されるアサーションとサブテストの数を設定するために使用されます。実行されたアサーションとサブテストの数が期待される数と一致しない場合、テストは失敗します。

注: アサーションが追跡されるようにするには、`assert` を直接使用する代わりに `t.assert` を使用する必要があります。

test('top level test', (t) => {
  t.plan(2);
  t.assert.ok('some relevant assertion here');
  t.test('subtest', () => {});
}); 

非同期コードを扱う場合、`plan` 関数を使用して、正しい数のアサーションが実行されることを保証できます。

test('planning with streams', (t, done) => {
  function* generate() {
    yield 'a';
    yield 'b';
    yield 'c';
  }
  const expected = ['a', 'b', 'c'];
  t.plan(expected.length);
  const stream = Readable.from(generate());
  stream.on('data', (chunk) => {
    t.assert.strictEqual(chunk, expected.shift());
  });

  stream.on('end', () => {
    done();
  });
}); 

`wait` オプションを使用すると、テストが期待されるアサーションを待つ時間を制御できます。たとえば、最大待機時間を設定すると、テストは指定された時間内に非同期アサーションが完了するのを待つようになります。

test('plan with wait: 2000 waits for async assertions', (t) => {
  t.plan(1, { wait: 2000 }); // Waits for up to 2 seconds for the assertion to complete.

  const asyncActivity = () => {
    setTimeout(() => {
      t.assert.ok(true, 'Async assertion completed within the wait time');
    }, 1000); // Completes after 1 second, within the 2-second wait time.
  };

  asyncActivity(); // The test will pass because the assertion is completed in time.
}); 

注: `wait` タイムアウトが指定された場合、それはテスト関数が実行を終えた後にのみカウントダウンを開始します。

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> サブテストの名前。テスト結果を報告する際に表示されます。デフォルト: `fn` の `name` プロパティ、または `fn` に名前がない場合は `'<anonymous>'`。
  • options <Object> サブテストの設定オプション。以下のプロパティがサポートされています。
    • concurrency <number> | <boolean> | <null> 数値が提供された場合、その数のテストがアプリケーションスレッド内で並列に実行されます。`true` の場合、すべてのサブテストが並列に実行されます。`false` の場合、一度に1つのテストのみが実行されます。指定されない場合、サブテストはこの値を親から継承します。デフォルト: `null`。
    • only <boolean> truthyで、かつテストコンテキストが only テストを実行するように設定されている場合、このテストが実行されます。それ以外の場合、テストはスキップされます。デフォルト: false
    • signal <AbortSignal> 進行中のテストを中止できるようにします。
    • skip <boolean> | <string> truthyな場合、テストはスキップされます。文字列が指定された場合、その文字列はテスト結果でテストをスキップした理由として表示されます。デフォルト: false
    • todo <boolean> | <string> truthyな場合、テストは TODO としてマークされます。文字列が指定された場合、その文字列はテスト結果でテストが TODO である理由として表示されます。デフォルト: false
    • timeout <number> テストがこのミリ秒数を超えると失敗します。指定されていない場合、サブテストはこの値を親から継承します。デフォルト: Infinity
    • plan <number> テストで実行が期待されるアサーションとサブテストの数。テストで実行されたアサーションの数がプランで指定された数と一致しない場合、テストは失敗します。デフォルト: undefined
  • fn <Function> | <AsyncFunction> テスト対象の関数。この関数の最初の引数は TestContext オブジェクトです。テストがコールバックを使用する場合、コールバック関数は2番目の引数として渡されます。デフォルト: 何もしない関数。
  • 戻り値: <Promise> テストが完了すると `undefined` で解決されます。

この関数は、現在のテストの下にサブテストを作成するために使用されます。この関数は、トップレベルの `test()` 関数と同じように動作します。

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

context.waitFor(condition[, options])#

  • condition <Function> | <AsyncFunction> 正常に完了するか、定義されたポーリングタイムアウトが経過するまで定期的に呼び出されるアサーション関数。正常な完了とは、スローまたはリジェクトしないことと定義されます。この関数は引数を受け取らず、任意の値を返すことが許可されています。
  • options <Object> ポーリング操作のためのオプションの設定オブジェクト。以下のプロパティがサポートされています。
    • interval <number> `condition` の呼び出しが失敗した後、再度試行するまでに待機するミリ秒数。デフォルト: `50`。
    • timeout <number> ポーリングタイムアウト (ミリ秒)。この時間が経過するまでに `condition` が成功しなかった場合、エラーが発生します。デフォルト: `1000`。
  • 戻り値: <Promise> `condition` によって返された値で解決されます。

このメソッドは、`condition` 関数が正常に返るか、操作がタイムアウトするまで、その関数をポーリングします。

クラス: `SuiteContext`#

`SuiteContext` のインスタンスは、テストランナーと対話するために各スイート関数に渡されます。ただし、`SuiteContext` コンストラクタは API の一部として公開されていません。

context.filePath#

現在のスイートを作成したテストファイルの絶対パス。テストファイルがスイートを生成する追加のモジュールをインポートする場合、インポートされたスイートはルートテストファイルのパスを返します。

context.name#

スイートの名前。

context.signal#

テストが中止されたときにテストのサブタスクを中止するために使用できます。