【JavaScript】assert関数でコードの正確性を確認する|console.assert・Node.js assert・テストフレームワークまで徹底解説

JavaScriptでコードの正確性を確認するために欠かせないassert(アサーション)。開発中のデバッグからテスト駆動開発(TDD)まで、あらゆる場面で活用される重要な仕組みです。

この記事では、console.assert() の基本構文から自作 assert 関数の実装、Node.js の assert モジュール、Jest や Mocha でのアサーション、さらには TypeScript の asserts 型述語まで、assert に関するすべてを体系的に解説します。

この記事で学べること

  • console.assert() の基本構文と動作
  • 自作 assert 関数の実装方法
  • Node.js assert モジュールの主要メソッド
  • assertconsole.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.jsNode.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)
非同期rejectexpect(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デバッグ
自作 assertError スローどこでも前提条件の保証
Node.js assertAssertionErrorNode.jsテスト
Jest expectテスト失敗テスト環境ユニットテスト
TS asserts 型述語Error スロー + 型絞り込みTypeScript型安全なバリデーション

実務での使い分け指針

  • デバッグ目的なら console.assert() で軽くチェック
  • 絶対に守るべき前提条件には自作 assert(Error スロー)
  • テストコードでは Jest の expect や Node.js の assert モジュールを使用
  • TypeScript では asserts 型述語で型安全性とランタイムチェックを両立
  • 本番環境では環境変数やビルドツールで assert の動作を制御

assert は「コードの前提条件を明示する」という、プログラミングにおいて非常に重要な役割を担います。デバッグ、テスト、型安全性の確保と、適切な場面で適切な assert を使い分けて、信頼性の高いコードを書いていきましょう。