Node.js Assert モジュール
1. Assert モジュールとは?
Assert モジュールは、コード内の不変条件(Invariants)を検証するための、シンプルかつ強力なアサーションテスト機能を提供します。
これは Node.js のコアモジュールであり、別途インストールする必要はありません。
主な機能は以下の通りです:
- Truthy/Falsy な値のシンプルなアサーション
- 厳密(Strict)および緩い(Loose)等価性チェック
- オブジェクトのディープ比較(Deep Comparison)
- エラーのスロー(Throw)とハンドリング
- async/await パターンのサポート
注意: Jest や Mocha のようなテスティングフレームワークほど多機能ではありませんが、Assert モジュールは非常に軽量であり、シンプルなテストのニーズや外部依存関係を避けたい場合に最適です。
2. Assert の導入
以下は、Assert モジュールを使用してシンプルな関数をテストするクイック例です。
2.1 基本的なアサーションの例
const assert = require('assert').strict;
// テスト対象の関数
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('入力値は数値である必要があります');
}
return a + b;
}
// テストケース
assert.strictEqual(add(2, 3), 5, '2 + 3 は 5 になるべきです');
// エラーケースのテスト
assert.throws(
() => add('2', 3),
TypeError,
'数値以外の入力に対しては TypeError をスローすべきです'
);
console.log('すべてのテストをパスしました!');3. インポートとセットアップ
Node.js アプリケーションで Assert モジュールをインポートして使用する方法はいくつかあります。
3.1 CommonJS によるインポート (Node.js)
// 基本的な require
const assert = require('assert');
// 厳密モード(推奨)の使用
const assert = require('assert').strict;
// 特定のメソッドをデストラクチャリング
const { strictEqual, deepStrictEqual, throws } = require('assert');
// 非同期テスト用
const { rejects, doesNotReject } = require('assert').strict;3.2 ES Modules によるインポート (Node.js 12+)
// デフォルトインポートの使用
import assert from 'assert';
// ESM で厳密モードを使用する場合
import { strict as assert } from 'assert';
// 特定のメソッドをインポート
import { strictEqual, deepStrictEqual } from 'assert';
// ダイナミックインポート
const { strict: assert } = await import('assert');ベストプラクティス: より正確な比較と分かりやすいエラーメッセージを提供するため、厳密モード(Strict Mode)の使用が推奨されます。また、将来の Node.js バージョンでは厳密モードがデフォルトになる予定です。
4. コア・アサーションメソッド
Assert モジュールは、コード内の値についてアサーションを行うための複数のメソッドを提供します。これらのメソッドは、Assert モジュールを使用したテストの基礎となります。
4.1 assert(value[, message])
値が Truthy かどうかをテストします。値が Falsy な場合、AssertionError がスローされます。
const assert = require('assert');
// これらはパスします
assert(true);
assert(1);
assert('string');
assert({});
try {
// これは AssertionError をスローします
assert(false, 'この値は Truthy ではありません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}
try {
// これらもエラーをスローします
assert(0);
assert('');
assert(null);
assert(undefined);
} catch (err) {
console.error(`エラー: ${err.message}`);
}4.2 assert.ok(value[, message])
これは assert() のエイリアスです。
const assert = require('assert');
// これらのアサーションは等価です
assert.ok(true, 'この値は Truthy です');
assert(true, 'この値は Truthy です');5. 値の比較
Assert モジュールは、型変換(Coercion)やオブジェクト比較に関する挙動が異なる、複数の値比較方法を提供します。
5.1 assert.equal(actual, expected[, message])
等価演算子(==)を使用して、actual と expected の間の浅い(Shallow)、型変換を伴う等価性をテストします。
const assert = require('assert');
// これらはパスします(型変換を伴う等価性)
assert.equal(1, 1);
assert.equal('1', 1); // 文字列が数値に変換されます
assert.equal(true, 1); // 真偽値が数値に変換されます
try {
// これはエラーをスローします
assert.equal(1, 2, '1 は 2 と等しくありません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}5.2 assert.strictEqual(actual, expected[, message])
厳密等価演算子(===)を使用して、actual と expected の間の厳密な等価性をテストします。
const assert = require('assert');
// これはパスします
assert.strictEqual(1, 1);
try {
// これらはエラーをスローします(厳密な等価性)
assert.strictEqual('1', 1, '文字列の "1" は数値の 1 と厳密には等しくありません');
assert.strictEqual(true, 1, 'true は 1 と厳密には等しくありません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}ベストプラクティス: 予期しない型変換の問題を避けるため、equal() よりも strictEqual() を使用することをお勧めします。
6. オブジェクトと配列の比較
オブジェクトや配列を扱う場合、参照だけでなく、その中身を比較するためにディープチェック(Deep Check)を行う必要があります。
6.1 assert.deepEqual(actual, expected[, message])
緩い等価性(==)を用いて、actual と expected の間のディープな等価性をテストします。
6.2 assert.deepStrictEqual(actual, expected[, message])
厳密な等価性(===)を用いて、actual と expected の間のディープな等価性をテストします。
const assert = require('assert');
// 同じ構造を持つオブジェクト
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { a: 1, b: { c: 2 } };
const obj3 = { a: '1', b: { c: '2' } };
// これらはパスします
assert.deepEqual(obj1, obj2);
assert.deepStrictEqual(obj1, obj2);
// これはパスします(緩い等価性)
assert.deepEqual(obj1, obj3);
try {
// これはエラーをスローします(厳密な等価性)
assert.deepStrictEqual(obj1, obj3, 'オブジェクトが厳密にはディープ等価ではありません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}
// 配列
const arr1 = [1, 2, [3, 4]];
const arr2 = [1, 2, [3, 4]];
const arr3 = ['1', '2', ['3', '4']];
// これらはパスします
assert.deepEqual(arr1, arr2);
assert.deepStrictEqual(arr1, arr2);
// これはパスします(緩い等価性)
assert.deepEqual(arr1, arr3);
try {
// これはエラーをスローします(厳密な等価性)
assert.deepStrictEqual(arr1, arr3, '配列が厳密にはディープ等価ではありません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}7. 不等価と否定
等価性をチェックするのと同様に、値が等しくないことを検証することも重要です。
7.1 assert.notEqual(actual, expected[, message])
不等価演算子(!=)を使用して、浅い型変換を伴う不等価性をテストします。
7.2 assert.notStrictEqual(actual, expected[, message])
厳密不等価演算子(!==)を使用して、厳密な不等価性をテストします。
const assert = require('assert');
// これらはパスします
assert.notEqual(1, 2);
assert.notStrictEqual('1', 1);
try {
// これはエラーをスローします
assert.notEqual(1, '1', '1 は "1" と型変換を含めると等しくなります');
} catch (err) {
console.error(`エラー: ${err.message}`);
}
try {
// これはエラーをスローします
assert.notStrictEqual(1, 1, '1 は 1 と厳密に等しいです');
} catch (err) {
console.error(`エラー: ${err.message}`);
}7.3 ディープ不等価
- assert.notDeepEqual(actual, expected[, message]):緩い不等価性でディープな不等価性をテストします。
- assert.notDeepStrictEqual(actual, expected[, message]):厳密な不等価性でディープな不等価性をテストします。
const assert = require('assert');
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 1, b: 3 };
const obj3 = { a: '1', b: '2' };
// これらはパスします
assert.notDeepEqual(obj1, obj2);
assert.notDeepStrictEqual(obj1, obj2);
assert.notDeepStrictEqual(obj1, obj3);
try {
// これはエラーをスローします(緩い等価性)
assert.notDeepEqual(obj1, obj3, 'obj1 は obj3 と緩やかなディープ等価です');
} catch (err) {
console.error(`エラー: ${err.message}`);
}8. エラーハンドリング
コードが期待通りのエラーをスローすることをテストすることは、堅牢なアプリケーションを作成する上で不可欠です。
8.1 assert.throws(fn[, error][, message])
関数 fn がエラーをスローすることを期待します。スローされない場合、AssertionError がスローされます。
const assert = require('assert');
// エラーをスローする関数
function throwingFunction() {
throw new Error('エラー発生');
}
// これはパスします
assert.throws(throwingFunction);
// 特定のエラーメッセージを確認
assert.throws(
throwingFunction,
/エラー発生/,
'予期しないエラーメッセージです'
);
// 特定のエラー型を確認
assert.throws(
throwingFunction,
Error,
'間違ったエラータイプです'
);
// 検証関数による確認
assert.throws(
throwingFunction,
function(err) {
return err instanceof Error && /発生/.test(err.message);
},
'エラーの検証に失敗しました'
);
try {
// これは AssertionError をスローします
assert.throws(() => {
// この関数はスローしません
return 'エラーなし';
}, '関数がスローすることを期待していました');
} catch (err) {
console.error(`エラー: ${err.message}`);
}8.2 assert.doesNotThrow(fn[, error][, message])
関数 fn がエラーをスローしないことを期待します。スローされた場合、そのエラーは伝播(プロパゲート)されます。
const assert = require('assert');
// これはパスします
assert.doesNotThrow(() => {
return 'エラーなし';
});
try {
// これは元のエラーをスローします
assert.doesNotThrow(() => {
throw new Error('これがスローされます');
}, '予期しないエラー');
} catch (err) {
console.error(`エラー: ${err.message}`);
}9. 非同期コードのテスト
現代の JavaScript では、非同期パターンが多用されます。Assert モジュールは、Promise ベースおよびコールバックベースの両方の非同期コードをテストするためのユーティリティを提供します。
9.1 assert.rejects(asyncFn[, error][, message])
asyncFn の Promise または非同期関数を待機し、それが拒否(Reject)されることを期待します。
const assert = require('assert');
async function asyncTest() {
// 拒否される Promise を返す関数
function failingAsyncFunction() {
return Promise.reject(new Error('非同期エラー'));
}
// これはパスします
await assert.rejects(
failingAsyncFunction(),
/非同期エラー/
);
// これもパスします
await assert.rejects(
async () => {
throw new Error('非同期関数のエラー');
},
{
name: 'Error',
message: '非同期関数のエラー'
}
);
try {
// これは AssertionError をスローします
await assert.rejects(
Promise.resolve('成功'),
'Promise が拒否されることを期待していました'
);
} catch (err) {
console.error(`エラー: ${err.message}`);
}
}
// 非同期テストを実行
asyncTest().catch(err => console.error(`未ハンドリングエラー: ${err.message}`));9.2 assert.doesNotReject(asyncFn[, error][, message])
asyncFn の Promise または非同期関数を待機し、それが解決(Fulfill)されることを期待します。
const assert = require('assert');
async function asyncTest() {
// これはパスします
await assert.doesNotReject(
Promise.resolve('成功')
);
// これもパスします
await assert.doesNotReject(
async () => {
return '非同期関数の成功';
}
);
try {
// これは元の拒否理由(Rejection Reason)をスローします
await assert.doesNotReject(
Promise.reject(new Error('失敗')),
'Promise が解決することを期待していました'
);
} catch (err) {
console.error(`エラー: ${err.message}`);
}
}
// 非同期テストを実行
asyncTest().catch(err => console.error(`未ハンドリングエラー: ${err.message}`));10. その他のアサーションメソッド
10.1 assert.match(string, regexp[, message])
入力文字列が正規表現にマッチすることを期待します。
const assert = require('assert');
// これはパスします
assert.match('I love Node.js', /Node\.js/);
try {
// これは AssertionError をスローします
assert.match('Hello World', /Node\.js/, '文字列がパターンにマッチしません');
} catch (err) {
console.error(`エラー: ${err.message}`);
}10.2 assert.fail([message])
指定されたメッセージ、またはデフォルトのメッセージで AssertionError をスローします。
const assert = require('assert');
try {
// これは常に AssertionError をスローします
assert.fail('このテストは常に失敗します');
} catch (err) {
console.error(`エラー: ${err.message}`);
}11. 厳密モード(Strict Mode)
Node.js は、すべての比較において厳密等価性を使用するアサーションの厳密モードを提供しています。予見可能な結果を得るために、厳密モードの使用が推奨されます。
// assert の厳密版をインポート
const assert = require('assert').strict;
// これらは等価です
assert.strictEqual(1, 1);
assert.equal(1, 1); // 厳密モードでは、これは strictEqual と同じです
// これらは等価です
assert.deepStrictEqual({ a: 1 }, { a: 1 });
assert.deepEqual({ a: 1 }, { a: 1 }); // 厳密モードでは、これは deepStrictEqual と同じです
try {
// 厳密モードでは、これはエラーをスローします
assert.equal('1', 1);
} catch (err) {
console.error(`エラー: ${err.message}`);
}