プログラミング学習 公開 2026.10.27

AIが生成したJavaScriptを自分で検証する|仕様・境界値・テストの実践手順

執筆・監修: 徐 聖博/株式会社シンシア 代表取締役社長

AIが生成したJavaScriptを自分で検証する|仕様・境界値・テストの実践手順

AIが出したコードを信じる前に、期待する動作を表にし、境界値・型・空配列をテストします。Nodeの標準機能だけで動く16件のテストと実行結果、わざと作った誤実装が「普通の値」では合格に見えてしまう例、テストが通っても確認が必要な範囲を解説します。

AIが出したコードは「動いた」では合格にしない。期待値を先に書いて、手元で確かめる

AIが出したコードは、実行できただけでは正しいと判断できません。AIに「このコードは正しいですか」と聞き直しても、返ってくるのはAI自身の評価です。「何を受け取り、何を返すべきか」を人が先に決め、その期待値と実行結果を比べて、仕様に合う証拠を手元に残します。

ここでは「点数の配列から合格点だけを取り出す」という小さな関数を使います。特定のAIが間違えたという実験ではありません。説明のために意図して作った誤実装と修正例を使う教材です。AIのアカウント、APIキー、受講者の個人データは使いません。必要なのはNode.jsだけです。

先に結果を書いておきます。仕様から作った16件のテストを、修正例に当てると16件すべて成功しました。誤実装に当てると、比較用の1件を除く15件のうち11件が失敗しました。ただし残りの4件は、誤実装でも成功します。「普通の値」「空の配列」だけを試していたら、この誤実装は合格に見えていたということです。

1. コードを読む前に、期待する動作を固定する

「80点以上を取り出して」だけでは、文字列の「90」や101点をどう扱うかが決まっていません。まず次の仕様を置きます。これは教材用の設計判断であり、点数を扱うすべてのシステムに共通する規則ではありません。

確認項目 この教材の仕様
入力全体 配列。配列以外はTypeErrorにする
有効な点数 0〜100の有限な数値。小数は許可する
合格点 指定したしきい値以上。既定は80
しきい値 0〜100の有限な数値。範囲外や文字列はRangeErrorにする
不正な配列要素 文字列、null、NaN、無限大、範囲外の値は結果から除外する
戻り値 元の順序と重複を保った別の配列
元データ 入力配列を変更しない

業務によっては、不正な要素を黙って除くのではなく、取り込み全体をエラーにすべき場合もあります。その場合はコードだけでなく、この仕様表とテストも変えます。先に選択を言葉にすることが大切です。

次に、コードを見ずに期待する結果を書きます。

入力 期待する結果 確認すること
[79, 80, 81] [80, 81] 境界の80を含む
["90", 90] [90] 文字列を勝手に数値へ変えない
[-1, 0, 100, 101] [100] 範囲外の点数を除く
[] [] 空でも処理できる
[100, 80, 100] [100, 80, 100] 順序と重複を維持する

条件式の読み方が不安なときは、if文と条件の作り方に戻ってから進めてください。

2. 「動くけれど間違う」例を確かめる

次は教材用の誤実装です。外部のAIが実際に出したコードではありません。

export const selectPassingScores = (scores, threshold = 80) =>
  scores.filter((score) => score > threshold);

[79, 80, 81] を渡すと [81] になり、80点が落ちます。仕様は「以上」なのに、条件は「より大きい」だからです。さらに ['90', 90] を渡すと、意図していない文字列の値も通ります。JavaScriptは '90' > 80 を比べるときに文字列を数値へ変えるためです。

例外が発生しなくても、期待値と違えば不合格です。エラー表示がある場合の調べ方はJavaScriptのエラーの読み方で確認できますが、エラーが出ないミスは仕様と照合して探します。

3. 修正例を仕様と対応させる

次のコードを ai-code-review.mjs という名前で保存します。

export function selectPassingScores(scores, threshold = 80) {
  if (!Array.isArray(scores)) {
    throw new TypeError('scores must be an array');
  }
  if (!Number.isFinite(threshold) || threshold < 0 || threshold > 100) {
    throw new RangeError('threshold must be a finite number between 0 and 100');
  }
  return scores.filter((score) =>
    Number.isFinite(score) && score >= 0 && score <= 100 && score >= threshold
  );
}

入力全体の検査、しきい値の検査、各点数の検査を分けました。Number.isFinite は文字列を数値へ変換せず、有限な数値かを調べるために使っています。filter は条件を満たす要素を抽出する処理です。詳細な言語仕様は、MDNのNumber.isFiniteとArray.filterを確認してください。

>= threshold だけ直して終わらず、0〜100という有効範囲や、入力配列を壊さないことも確認します。「目についたバグを1つ直した」と「定義した仕様を確認した」は別です。

4. Nodeの標準テストで、仕様表を16件のテストにする

Nodeを実行できる自分の作業フォルダで試します。まず node --version でバージョンを記録してください。この記事の実行結果は、Node v24.22.1(macOS)で2026年10月27日に実行したものです。別の環境では、その環境で再実行した結果を残します。

同じフォルダに ai-code-review.test.mjs を保存します。仕様表の行ごとに、テストを1件以上対応させています。

import test from 'node:test';
import assert from 'node:assert/strict';
import { selectPassingScores } from './ai-code-review.mjs';

test('通常値: 80以上を入力順で返す', () => {
  assert.deepEqual(selectPassingScores([70, 85, 90]), [85, 90]);
});
test('境界値: 79は不合格、80と81は合格', () => {
  assert.deepEqual(selectPassingScores([79, 80, 81]), [80, 81]);
});
test('文字列の90を数値へ暗黙変換しない', () => {
  assert.deepEqual(selectPassingScores(['90', 90]), [90]);
});
test('null・undefined・真偽値を無視する', () => {
  assert.deepEqual(selectPassingScores([null, undefined, true, false, 80]), [80]);
});
test('NaNと無限大を除外する', () => {
  assert.deepEqual(selectPassingScores([NaN, Infinity, -Infinity, 99]), [99]);
});
test('有効範囲の外を除外する', () => {
  assert.deepEqual(selectPassingScores([-1, 0, 100, 101]), [100]);
});
test('空配列には空配列を返す', () => {
  assert.deepEqual(selectPassingScores([]), []);
});
test('重複と入力順を維持する', () => {
  assert.deepEqual(selectPassingScores([100, 80, 100, 90]), [100, 80, 100, 90]);
});
test('入力配列を変更せず別配列を返す', () => {
  const source = Object.freeze([90, 70, 80]);
  const result = selectPassingScores(source);
  assert.deepEqual(source, [90, 70, 80]);
  assert.notEqual(result, source);
});
test('しきい値0を指定できる', () => {
  assert.deepEqual(selectPassingScores([-1, 0, 1, 100, 101], 0), [0, 1, 100]);
});
test('しきい値100を指定できる', () => {
  assert.deepEqual(selectPassingScores([99, 100, 101], 100), [100]);
});
test('小数の数値も仕様の範囲で判定する', () => {
  assert.deepEqual(selectPassingScores([79.9, 80, 80.1]), [80, 80.1]);
});
test('配列以外はTypeError', () => {
  for (const value of [null, undefined, 80, '80', {}]) {
    assert.throws(() => selectPassingScores(value), TypeError);
  }
});
test('不正なしきい値はRangeError', () => {
  for (const threshold of [-1, 101, NaN, Infinity, '80', null]) {
    assert.throws(() => selectPassingScores([80], threshold), RangeError);
  }
});
test('配列の穴は値として返さない', () => {
  const source = new Array(3); source[1] = 80;
  assert.deepEqual(selectPassingScores(source), [80]);
});
test('教材用の誤実装が境界値と型の仕様を破ることを確認', () => {
  // 説明用にわざと作った誤実装。AIの実際の出力ではない
  const wrong = (scores, threshold = 80) => scores.filter((score) => score > threshold);
  assert.notDeepEqual(wrong([79, 80, 81]), [80, 81]);
  assert.notDeepEqual(wrong(['90', 90]), [90]);
});

実行コマンドは次のとおりです。

node --test ai-code-review.test.mjs

修正例(3節のコード)で実行した結果の末尾です。

ℹ tests 16
ℹ suites 0
ℹ pass 16
ℹ fail 0

比較の仕組みはNodeのassert公式文書で確認できます。

テストを書くときの注意は、期待値を、検証対象の関数を使って計算しないことです。間違った関数から「正解」を作ると、実装とテストが同じ間違いを共有します。1節の表を基に、期待値は独立して書いてください。

誤実装に同じテストを当てると、どこで落ちるか

ai-code-review.mjs の中身を2節の誤実装に差し替えて、同じコマンドを実行しました。最後の1件は誤実装そのものを確かめる比較用なので、残りの15件で見ます。

結果 件数 テスト
失敗 11 境界値、文字列の90、null・真偽値、NaN・無限大、範囲外、重複と順序、しきい値0、しきい値100、小数、不正なしきい値、配列の穴
成功 4 通常値、空配列、入力配列を変更しない、配列以外はTypeError

成功した4件に注目してください。通常値 [70, 85, 90] には境界の80が含まれず、空配列はどんな条件でも空のまま返ります。「配列以外はTypeError」も、誤実装が入力を検査したからではありません。配列でない値に filter を呼ぼうとして、JavaScript自身がTypeErrorを出しただけです。

AIが書いたコードを、AIが出した「普通の例」だけで試すと、このような偶然の成功を合格と取り違えます。境界の値、型の違う値、仕様の範囲外の値を、自分で選んで足してください。

5. テストが通っても、確認していないことは残る

このテストで分かるのは、点数抽出関数が決めた仕様と入力例に合うことです。画面、データベース、認証、外部API、個人情報の保護、アクセス集中時の動作は確認していません。

例えば、ログインした利用者だけが見られる画面にこの関数を置いても、関数の試験だけでアクセス制御が正しいとは言えません。実際のサービスへ組み込むときは、そのサービスの受入条件と承認手順を別に確認します。AIに書かせたコードの認可漏れ・秘密情報・依存パッケージの確かめ方は、AI時代のセキュリティ講座で扱っています。

学習でのゴールは「何でも安全だと言えること」ではありません。「どの仕様を、どの入力で、どの結果まで確かめたか」を説明できることです。

6. 検証の記録を、成果物の説明に使う

次のように記録すると、あとから確認し直せます。この記事の実行で埋めると、こうなります。

対象: selectPassingScores
仕様: 0〜100の有限な数値だけを対象に、しきい値以上を抽出する
環境: Node v24.22.1(macOS)
実行: node --test ai-code-review.test.mjs
結果: 修正例 16件成功・0件失敗/誤実装 15件中11件失敗(比較用の1件を除く)
修正: > を >= に変更し、型と範囲の検査を追加
未確認: UI、認証、DB、外部API、性能

自分で試すときは、まだ実行していないのに成功数だけ埋めないでください。AIを使った部分、仕様を決めた部分、自分で確認した部分も分けて書きます。記録を応募用の成果物にまとめるときは、ポートフォリオとREADMEの作り方を使えます。

よくある質問

AIにテストも作らせれば、それで十分ですか?

十分とは限りません。実装とテストが同じ仕様の取り違えを含む可能性があるため、入力と期待値を先に人が確認します。AIのテスト案は、しきい値を79・80・81に変える、数値を文字列にする、配列を空にする、元データが変わらないかを見る、といった観点の抜けを探す補助として使ってください。

テストは何件あれば合格ですか?

件数だけでは決まりません。この記事では仕様表の行ごとにテストを置いた結果が16件でした。4節のとおり、件数があっても「普通の値」ばかりなら誤実装が通ります。実サービスでは、確認すべき振る舞いとリスクから項目を決めます。

エラーが表示されなければ、実装は正しいのではありませんか?

この教材の誤実装は、エラーを出さずに80点を落とします。実行できることと、仕様に合うことを分けて確認してください。

次に進むために

まず仕様表を1つ作り、境界値・型違い・空データの3ケースを自分で確かめてください。条件分岐や配列の基礎で手が止まるなら、JavaScript練習問題10問で、まず基礎の練習問題を確かめてから戻ってきてください。結果をREADMEに残したら、何を判断して直したのかを説明します。足りない基礎は無料講座の一覧から選べます。

参考資料

この記事をシェア

無料の学習講座で、いまから手を動かす

AI開発からWeb開発まで、未経験でも進められる講座を無料で公開しています。

会員登録(無料) すると、気になる求人の保存と講座の受講履歴の記録ができます。

この順番で合っているか、確認しませんか

学習の順番は、目指す職種によって変わります。いま考えている進め方を書いて送ってもらえれば、無料講座の内容と実際の求人要件に照らして返答します。

相談を書いて送る(無料・会員登録なし)

返答までお時間をいただくことがあります。

新着記事