JavaScriptでコードの正確性を確認するために欠かせないassert(アサーション)。開発中のデバッグからテスト駆動開発(TDD)まで、あらゆる場面で活用される重要な仕組みです。
この記事では、console.assert() の基本構文から自作 assert 関数の実装、Node.js の assert モジュール、Jest や Mocha でのアサーション、さらには TypeScript の asserts 型述語まで、assert に関するすべてを体系的に解説します。
この記事で学べること
console.assert() の基本構文と動作
- 自作 assert 関数の実装方法
- Node.js
assert モジュールの主要メソッド
assert と console.assert の決定的な違い
- Jest / Mocha でのアサーション
- TypeScript の
asserts 型述語
- 本番環境での assert の扱いと落とし穴
console.assert() の基本構文と使い方
console.assert() はブラウザと Node.js の両方で使える組み込みメソッドです。第1引数が falsy(偽値)の場合にコンソールへエラーメッセージを出力します。
基本構文
JavaScript
// 構文
console.assert(条件式, メッセージ);
// 条件がfalsy → コンソールにエラー出力
// 条件がtruthy → 何も起きない
基本的な使用例
JavaScript
const age = 25;
// 条件が true → 何も出力されない
console.assert(age >= 0, '年齢は0以上でなければなりません');
// 条件が false → エラーメッセージが出力される
console.assert(age >= 30, '年齢は30以上でなければなりません');
// Assertion failed: 年齢は30以上でなければなりません
// オブジェクトも出力可能
console.assert(false, 'エラー情報:', { code: 404, url: '/api/user' });
// Assertion failed: エラー情報: {code: 404, url: '/api/user'}
注意:console.assert() は条件が失敗してもプログラムの実行を停止しません。エラーメッセージを出力するだけで、後続の処理はそのまま続行されます。
falsy 値の一覧と assert の判定
console.assert() の第1引数は Boolean に変換されて評価されます。以下の値はすべて falsy と判定され、アサーションが失敗します。
JavaScript
// すべて Assertion failed になる
console.assert(false, 'false');
console.assert(0, '0');
console.assert('', '空文字列');
console.assert(null, 'null');
console.assert(undefined, 'undefined');
console.assert(NaN, 'NaN');
自作 assert 関数の実装
console.assert() はプログラムを停止しないため、条件が満たされない場合にエラーをスローして処理を中断したい場合は、自作の assert 関数を実装します。
シンプルな実装
JavaScript
function assert(condition, message) {
if (!condition) {
throw new Error(message || 'Assertion failed');
}
}
// 使用例
const value = 10;
assert(value > 0, '値は正の数でなければなりません'); // OK
assert(value > 100, '値は100より大きくなければなりません');
// Error: 値は100より大きくなければなりません
カスタムエラークラスを使った実装
実務では、通常のエラーとアサーションエラーを区別するためにカスタムエラークラスを使うのが効果的です。
JavaScript
class AssertionError extends Error {
constructor(message) {
super(message);
this.name = 'AssertionError';
}
}
function assert(condition, message) {
if (!condition) {
throw new AssertionError(message || 'Assertion failed');
}
}
// try-catch でアサーションエラーだけを捕捉
try {
assert(false, '想定外の状態です');
} catch (e) {
if (e instanceof AssertionError) {
console.error('アサーション違反:', e.message);
} else {
throw e; // その他のエラーは再スロー
}
}
型チェック付き assert 関数
JavaScript
function assertType(value, expectedType, name = 'value') {
const actualType = typeof value;
assert(
actualType === expectedType,
`${name} の型は ${expectedType} を期待しましたが ${actualType} でした`
);
}
function assertNotNull(value, name = 'value') {
assert(
value !== null && value !== undefined,
`${name} は null または undefined であってはなりません`
);
}
// 使用例
assertType('hello', 'string', 'username'); // OK
assertType(42, 'string', 'username');
// Error: username の型は string を期待しましたが number でした
Node.js の assert モジュール
Node.js には組み込みの assert モジュールが用意されています。ブラウザの console.assert() とは異なり、アサーションが失敗すると例外をスローしてプログラムを停止させます。
基本的な使い方
Node.js
const assert = require('assert');
// assert(value) — truthy チェック
assert(true); // OK
assert(1); // OK
assert('hello'); // OK
assert(0); // AssertionError!
主要メソッド一覧
| メソッド |
説明 |
assert(value) | value が truthy か検証 |
assert.strictEqual(a, b) | === で等値比較 |
assert.notStrictEqual(a, b) | !== で非等値比較 |
assert.deepStrictEqual(a, b) | オブジェクト・配列の深い比較 |
assert.throws(fn) | 関数がエラーをスローするか検証 |
assert.rejects(asyncFn) | 非同期関数がrejectするか検証 |
assert.ok(value) | assert(value) と同じ |
assert.fail(message) | 常にAssertionErrorをスロー |
strictEqual と deepStrictEqual の使い分け
Node.js
const assert = require('assert');
// === strictEqual: プリミティブ値の比較 ===
assert.strictEqual(1 + 1, 2); // OK
assert.strictEqual('hello', 'hello'); // OK
assert.strictEqual(1, '1'); // AssertionError!(型が違う)
// === deepStrictEqual: オブジェクト・配列の比較 ===
assert.deepStrictEqual(
{ name: '太郎', age: 25 },
{ name: '太郎', age: 25 }
); // OK(プロパティの値が一致)
assert.deepStrictEqual(
[1, 2, 3],
[1, 2, 3]
); // OK
// strictEqual ではオブジェクトの比較は参照が異なるので失敗
assert.strictEqual(
{ name: '太郎' },
{ name: '太郎' }
); // AssertionError!(参照が異なる)
throws と rejects の使い方
Node.js
const assert = require('assert');
// 同期関数がエラーをスローするか検証
assert.throws(
() => { throw new TypeError('型エラー'); },
TypeError // エラーの型を検証
); // OK
// エラーメッセージも検証可能
assert.throws(
() => { throw new Error('invalid input'); },
{ message: 'invalid input' }
); // OK
// 非同期関数の reject を検証
async function testAsync() {
await assert.rejects(
async () => {
throw new Error('API Error');
},
{ message: 'API Error' }
); // OK
}
assert と console.assert の違い
console.assert() と Node.js の assert モジュールは名前が似ていますが、動作が根本的に異なります。この違いを正しく理解することが重要です。
| 項目 |
console.assert() |
Node.js assert |
| 失敗時の動作 | コンソールに出力のみ | 例外をスローして停止 |
| 実行環境 | ブラウザ + Node.js | Node.js のみ |
| import/require | 不要(グローバル) | require('assert') |
| 比較メソッド | なし(boolean のみ) | strictEqual, deepStrictEqual 等 |
| 主な用途 | デバッグ時のログ出力 | テスト・バリデーション |
| 本番での利用 | 問題なし(軽量) | 注意が必要 |
動作の違いを確認
// --- console.assert: 実行が止まらない ---
console.assert(false, '失敗しました');
console.log('この行は実行される'); // 出力される
// --- Node.js assert: 実行が停止する ---
const assert = require('assert');
assert(false, '失敗しました'); // AssertionError!
console.log('この行は実行されない'); // 到達しない
テストフレームワークでの assert
実務では Jest や Mocha などのテストフレームワークに組み込まれたアサーション機能を使うのが一般的です。それぞれの書き方を確認しましょう。
Jest(expect)
Jest は expect() 関数とマッチャーの組み合わせでアサーションを記述します。
Jest テスト
// sum.test.js
const sum = require('./sum');
test('1 + 2 は 3 になる', () => {
// 等値チェック
expect(sum(1, 2)).toBe(3);
});
test('オブジェクトの一致', () => {
const user = { name: '太郎', age: 25 };
// オブジェクトの深い比較
expect(user).toEqual({ name: '太郎', age: 25 });
});
test('エラーがスローされる', () => {
expect(() => {
throw new Error('invalid');
}).toThrow('invalid');
});
test('真偽値のチェック', () => {
expect(true).toBeTruthy();
expect(null).toBeNull();
expect(undefined).toBeUndefined();
});
Mocha + assert
Mocha は Node.js の assert モジュールをそのまま使えます。
Mocha テスト
const assert = require('assert');
describe('Array', () => {
describe('#indexOf()', () => {
it('値が存在しない場合は -1 を返す', () => {
assert.strictEqual(
[1, 2, 3].indexOf(4),
-1
);
});
it('値が存在する場合はインデックスを返す', () => {
assert.strictEqual(
[1, 2, 3].indexOf(2),
1
);
});
});
});
Jest と Mocha/assert の対応表
| 目的 |
Jest(expect) |
Node.js assert |
| 値の等価 | expect(a).toBe(b) | assert.strictEqual(a, b) |
| オブジェクト比較 | expect(a).toEqual(b) | assert.deepStrictEqual(a, b) |
| 真偽値チェック | expect(a).toBeTruthy() | assert.ok(a) |
| 例外スロー | expect(fn).toThrow() | assert.throws(fn) |
| 非同期reject | expect(fn).rejects.toThrow() | assert.rejects(fn) |
assert のユースケース
assert はさまざまな場面で活用できます。代表的なユースケースを見ていきましょう。
1. デバッグ時の前提条件チェック
前提条件の検証
function divide(a, b) {
// 前提条件: 0で割ることはできない
console.assert(b !== 0, '0で割ることはできません');
return a / b;
}
function processItems(items) {
// 前提条件: 配列であること
console.assert(Array.isArray(items), 'items は配列でなければなりません');
// 前提条件: 空でないこと
console.assert(items.length > 0, 'items は空であってはなりません');
return items.map(item => item * 2);
}
2. 型チェック・バリデーション
関数の入力バリデーション
function createUser(name, email, age) {
// 引数の型と値をチェック
assert(typeof name === 'string' && name.length > 0,
'name は空でない文字列でなければなりません');
assert(email.includes('@'),
'email は有効なメールアドレスでなければなりません');
assert(typeof age === 'number' && age >= 0,
'age は0以上の数値でなければなりません');
return { name, email, age };
}
3. 状態遷移の検証
状態マシンの検証
class Order {
constructor() {
this.status = 'created';
}
pay() {
// 支払いは created 状態でのみ可能
assert(
this.status === 'created',
`支払いは created 状態でのみ可能です(現在: ${this.status})`
);
this.status = 'paid';
}
ship() {
// 出荷は paid 状態でのみ可能
assert(
this.status === 'paid',
`出荷は paid 状態でのみ可能です(現在: ${this.status})`
);
this.status = 'shipped';
}
}
assert が失敗した時の動作
assert の失敗時の動作は環境によって大きく異なります。それぞれの挙動を正確に理解しましょう。
ブラウザ(console.assert)の場合
ブラウザでの動作
console.log('1: 処理開始');
console.assert(false, 'アサーション失敗');
console.log('2: 処理続行'); // 実行される!
console.log('3: 処理完了'); // 実行される!
実行結果
1: 処理開始
Assertion failed: アサーション失敗
2: 処理続行
3: 処理完了
Node.js(assert モジュール)の場合
Node.js での動作
const assert = require('assert');
console.log('1: 処理開始');
assert.ok(false, 'アサーション失敗');
console.log('2: ここには到達しない');
実行結果
1: 処理開始
AssertionError [ERR_ASSERTION]: アサーション失敗
at Object.<anonymous> (test.js:4:8)
at Module._compile (...)
TypeScript の asserts 型述語
TypeScript 3.7 で導入された asserts 型述語を使うと、assert 関数で型を絞り込む(型ガード)ことができます。これによりランタイムのチェックと型安全性を両立できます。
基本的な asserts 型述語
TypeScript
// asserts condition — 条件が true であることを保証
function assert(
condition: unknown,
message?: string
): asserts condition {
if (!condition) {
throw new Error(message ?? 'Assertion failed');
}
}
// 使用例
function processValue(value: string | null) {
assert(value !== null, 'value は null であってはなりません');
// ここで value は string 型に絞り込まれる
console.log(value.toUpperCase()); // OK: string 型として扱える
}
asserts value is Type
asserts value is Type を使うと、特定の型であることを保証する assert 関数を作れます。
TypeScript
interface User {
name: string;
email: string;
}
// value が User 型であることを保証する assert 関数
function assertIsUser(value: unknown): asserts value is User {
if (
typeof value !== 'object' ||
value === null ||
!('name' in value) ||
!('email' in value)
) {
throw new Error('User型ではありません');
}
}
// 使用例
function greet(data: unknown) {
assertIsUser(data);
// この時点で data は User 型
console.log(`こんにちは、${data.name}さん!`);
}
ポイント:asserts 型述語は、is 型ガード(value is Type)と異なり、戻り値が void です。関数が正常にリターンした場合に、型が保証されたとコンパイラが判断します。
本番環境での assert の扱い
assert は開発時のデバッグやテストに非常に便利ですが、本番環境ではどう扱うべきかを明確にしておくことが重要です。
console.assert() は残しても問題なし
console.assert() は条件が true の場合は何も行わず、パフォーマンスへの影響はごくわずかです。本番環境に残しておいても大きな問題にはなりません。
自作 assert / Node.js assert は条件付きで使う
本番環境対応の assert
// 環境変数で assert の有効/無効を切り替え
const isDev = process.env.NODE_ENV !== 'production';
function assert(condition, message) {
if (isDev && !condition) {
throw new Error(`[DEV] Assertion failed: ${message}`);
}
}
// 開発環境: エラーをスロー
// 本番環境: 何もしない(パフォーマンスに影響なし)
ビルドツールで assert を除去する
Webpack や Vite などのビルドツールでは、デッドコード除去(tree shaking)を利用して本番ビルドから assert を完全に除去できます。
webpack.config.js
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
]
};
注意:assert を本番で例外スローする設計にする場合、ユーザーが直面するエラーとなるため、適切なエラーハンドリング(try-catch やエラーバウンダリ)を必ず実装してください。
よくある落とし穴
assert を使う際にハマりやすいポイントをまとめます。
1. console.assert() が実行を止めると思い込む
NG パターン
function processData(data) {
// NG: console.assert は実行を止めない!
console.assert(data !== null, 'data は null です');
// data が null でも以下が実行されてしまう
return data.toString(); // TypeError!
}
// OK: 停止が必要なら自作 assert を使う
function processDataSafe(data) {
assert(data !== null, 'data は null です'); // Error をスロー
return data.toString(); // null の場合はここに到達しない
}
2. == と === の混同(Node.js assert)
equal vs strictEqual
const assert = require('assert');
// assert.equal() は == で比較(非推奨!)
assert.equal(1, '1'); // OK(型変換されてしまう)
// assert.strictEqual() は === で比較(推奨)
assert.strictEqual(1, '1'); // AssertionError!(正しく検出)
注意:assert.equal() と assert.deepEqual() は非推奨(deprecated)です。常に assert.strictEqual() と assert.deepStrictEqual() を使ってください。
3. assert で副作用のある式を書く
副作用の危険
let count = 0;
// NG: assert の中で副作用(count++)を使う
console.assert(++count === 1, 'count が不正です');
// assert を除去すると count のインクリメントも消える!
// OK: 副作用は assert の外で行う
count++;
console.assert(count === 1, 'count が不正です');
4. 非同期処理での assert の漏れ
非同期の落とし穴
const assert = require('assert');
// NG: Promise 内の assert はキャッチされない可能性がある
setTimeout(() => {
assert.strictEqual(1, 2); // UnhandledPromiseRejection
}, 1000);
// OK: assert.rejects を使う
async function testAsync() {
const result = await fetchData();
assert.strictEqual(result.status, 200);
}
実務で使える assert パターン
最後に、実務で役立つ assert のパターンをいくつか紹介します。
1. API レスポンスのバリデーション
API レスポンス検証
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const data = await response.json();
// レスポンスの構造を検証
assert(data !== null, 'APIレスポンスがnullです');
assert(typeof data.id === 'number', 'idが数値ではありません');
assert(typeof data.name === 'string', 'nameが文字列ではありません');
return data;
}
2. 設定値の検証
設定値の検証
function initApp(config) {
// 必須設定の検証
assert(config.apiUrl, 'API URLが設定されていません');
assert(config.apiKey, 'API Keyが設定されていません');
assert(
config.timeout > 0 && config.timeout <= 30000,
`timeout は 1〜30000ms の範囲で指定してください(現在: ${config.timeout})`
);
// アプリ起動
console.log('アプリを起動しました');
}
3. exhaustive チェック(網羅性の保証)
switch 文ですべてのケースを処理したことを保証するパターンです。新しいケースが追加された際にバグを防止できます。
exhaustive チェック
function assertNever(value) {
throw new Error(`未処理のケース: ${JSON.stringify(value)}`);
}
function getStatusMessage(status) {
switch (status) {
case 'loading': return '読み込み中...';
case 'success': return '完了';
case 'error': return 'エラーが発生しました';
default: assertNever(status);
}
}
// 'pending' が追加された場合、assertNever でエラーになる
// → 開発者が switch 文の修正を忘れることを防止
まとめ
JavaScript における assert の全体像を振り返りましょう。
| 手段 |
失敗時 |
環境 |
適した用途 |
console.assert() | ログ出力のみ | ブラウザ / Node.js | デバッグ |
| 自作 assert | Error スロー | どこでも | 前提条件の保証 |
| Node.js assert | AssertionError | Node.js | テスト |
| Jest expect | テスト失敗 | テスト環境 | ユニットテスト |
| TS asserts 型述語 | Error スロー + 型絞り込み | TypeScript | 型安全なバリデーション |
実務での使い分け指針
- デバッグ目的なら
console.assert() で軽くチェック
- 絶対に守るべき前提条件には自作 assert(Error スロー)
- テストコードでは Jest の
expect や Node.js の assert モジュールを使用
- TypeScript では
asserts 型述語で型安全性とランタイムチェックを両立
- 本番環境では環境変数やビルドツールで assert の動作を制御
assert は「コードの前提条件を明示する」という、プログラミングにおいて非常に重要な役割を担います。デバッグ、テスト、型安全性の確保と、適切な場面で適切な assert を使い分けて、信頼性の高いコードを書いていきましょう。