Skip to content

test: 実装の書き方ではなく画面とステップ列の仕様を確かめる - #68

Merged
shin4488 merged 1 commit into
mainfrom
test/spec-based-tests
Oct 26, 2026
Merged

shin4488 merged 1 commit into
mainfrom
test/spec-based-tests

Conversation

@shin4488

Copy link
Copy Markdown
Owner

既存のテストのうち、実装の書き方に依存していたものと、仕様違反を見逃していたものを書き換えました。公開された入口(ステップを作る関数と画面)から観測できる結果を、仕様と手計算から導いた期待値で確かめます。本体のコードは変えていません。

仕様・設計の変更なし。

書き換えたテスト

src/__tests__/visualizer.spec.ts

テスト 実装に依存していた点 確かめる仕様
バブルソート 比較・交換の i と j の順序(画面では2本を区別しない) 隣り合う2本を比べ、交換の直前に同じ2本を比べる。順序は問わない
選択ソート 比較の j が最小値候補であること。交換は n-1 回以下とだけ確認 各パスの最小値をパスの先頭へ移す交換の組(手計算: [5, 3, 4, 1, 2] で [0,3] [1,4] [2,4] [3,4])。最小値がすでに先頭にあるパスでは交換しない([1, 3, 2] で [1,2] だけ)。各パスで、未ソートの残りの棒すべてを候補と比べる
マージソート 1件のテストで、本体の添字の計算を再現していた。画面が使わない write.id で安定性を確かめていた 5件に分けた。昇順に書き戻し、入力を変えない/0・1個ではステップを作らない/マージの開始時に、表示中の範囲を分け目で分けた左右(それぞれ昇順)を示す/左右の先頭の棒を比べてから小さい方、同じ値なら左を取る/全部取ってから範囲へ書き戻す
クイックソート 「range の直後は pivot」「swap の後3ステップ以内に boundary」など、ステップの並び方を固定していた ピボットは範囲の右端/左の候補はピボットより大きく、右の候補は小さく、候補どうしを入れ替える/境界の左はピボット以下/ピボットを置いた位置の左はピボット以下、右は以上。左右の走査がピボットと同じ値を通る入力 [2, 3, 1, 2, 2] を加えた

src/__tests__/ui.spec.tsx

テスト 実装に依存していた点 確かめる仕様
スライダーの5件 getAllByRole('slider') の何番目か(操作の並び) 名前(本数:・アニメ速度:)で取る
表示順の並べ替え Mantine の Chip の DOM(nextElementSibling) チェックボックスの名前の並び
全選択 1番目のチェックボックス バブルソート のチェックボックス
凡例 画面のどこかに1つ以上あるか(パネルの凡例の欠落を見逃す) パネルごとに、ステップ: 0 と凡例の各項目を表示する
シャッフル Math.random の呼び出し回数 本数を21にしてシャッフルすると、並びが変わり、1〜21を1回ずつ含み、4つのパネルで同じ並びになる
オーバーレイ インラインの style.display の値 表示されているか(要素がないときも非表示とみなす)
マージの説明 再生の9・2・2ステップ目という位置 左 [1]・右 [2]・結果 [] → 「左の1を結果に入れました。」 → 結果 [1, 2] で書き戻し、の順に表示する
高さの基準 Math.random() = 0 で Fisher–Yates が作る並び [2, 3, 4, 5, 1] 最大値5が一時的に消えている間も、各棒の高さが「値 ÷ 5」のまま
比較枠 Math.random() = 0.999 で昇順になること、9000ms で完了すること 昇順の5本で、一時停止中は比較枠が残り、バブルソートが完了した時点で消える(ほかのパネルは再生中)

具体的な並びが必要な3件(マージの説明・高さの基準・比較枠)は、Math.random に固定値を与える代わりに、条件を満たすまでシャッフルボタンを押して並びを作ります。シャッフルの実装を変えても(例: Fisher–Yates を左から進める)落ちません。

UIテストの Math.random は、beforeEach で固定の擬似乱数(Park–Miller の線形合同法)に置き換えました。これまで多くのテストは実行のたびに違う並びで動いており、並びによってだけ起きる失敗を再現できませんでした。

消した確認

テストケースは消していません。テストの中の次の確認を、別の確認に置き換えました。

消した確認 理由
シャッフルで Math.random が呼ばれたこと 乱数の使い方は実装の詳細。並びが変わることを直接確かめる
マージソートの write.id と、id から求めた並び 画面は id を使わない。安定性は、同じ値のとき左を取ることで確かめる
選択ソートの交換は n-1 回以下 手計算した交換の組で、回数を含めて確かめる
選択ソートの比較は n(n-1)/2 回 各パスで残りの棒すべてを1回ずつ比べることで、同じ内容を確かめる

検証

すべて Docker のコンテナで実行しました。

  • yarn typecheck && yarn lint && yarn format && yarn test: 成功(44件)。
  • 本体を一時的に変えて、書き換え前と後のテストを実行しました。終了後に git diff で本体に差分がないことを確かめました。
本体の一時的な変更 種類 書き換え前 書き換え後
クイック: 左の走査がピボットと同じ値で止まる 不具合 通る 落ちる
クイック: 右の走査がピボットと同じ値で止まる 不具合 通る 落ちる
クイック: ピボットを置いた後の強調を右端に出す 不具合 通る 落ちる
クイック: 境界を左の候補の1つ右に置く 不具合 通る 落ちる
クイック: ピボットを左端にする 不具合 落ちる 落ちる
マージ: 同じ値のとき右を取る 不具合 落ちる 落ちる
マージ: マージの前に範囲を表示し直さない 不具合 落ちる 落ちる
選択: 最小値が先頭にあっても交換する 不具合 通る 落ちる
棒の高さの基準を表示中の最大値にする 不具合 落ちる 落ちる
完了したパネルに比較枠を残す 不具合 落ちる 落ちる
シャッフルで今の並びを使い回す 不具合 落ちる 落ちる
バブルの凡例から「入れ替え/比較」を消す 不具合 通る 落ちる
マージの説明で書き戻し中の文を出さない 不具合 落ちる 落ちる
選択: 比較の i と j を入れ替える 仕様を変えない 落ちる 通る
バブル: 比較・交換の i と j を入れ替える 仕様を変えない 落ちる 通る
マージ: write.id を0にする 仕様を変えない 落ちる 通る
範囲がないときオーバーレイを描画しない 仕様を変えない 落ちる 通る
速度の操作を本数の操作の前に置く 仕様を変えない 落ちる 通る
シャッフルを左から進む Fisher–Yates にする 仕様を変えない 落ちる 通る

「完了したパネルに比較枠を残す」は、書き換え前は完了まで再生するテストでも落ちることがありましたが、乱数で決まる並びで、バブルソートの最後の操作が比較になったときだけでした。書き換え後は比較枠のテストが決まった並びで検出します。

気付いた点(変更していない)

1回の再生で、sort_finish がアルゴリズムによって複数回送られます。src/App.tsx の送信処理は、どれかのパネルが完了するたびに、完了済みのパネルすべての分を送り直すためです。一時的なテストで ReactGA.event の呼び出しを数えると、20本で1回再生したとき(並びは乱数を固定していない)、quick_sort 3回、bubble_sort 2回、selection_sort 2回、merge_sort 1回でした。AGENTS.md には algorithm_type の値の約束だけがあり、回数の記載はないため、不具合かどうかは判断していません。

🤖 Generated with Claude Code

リファクタやライブラリの更新だけで落ちるテストと、仕様違反を見逃すテストがあったため、公開された入口から観測できる結果を、仕様と手計算から導いた期待値で確かめる形に書き換えた。本体のコードは変えていない。

| 対象 | 実装に依存していた点 | 確かめる仕様 |
| --- | --- | --- |
| バブル・選択ソート | 比較・交換の i と j の順序 | 2本の組(順序は問わない)、各パスの最小値をパスの先頭へ移す交換 |
| マージソート | 添字の計算の再現、UIが使わない write.id | 範囲の左右を昇順で示す、小さい方(同じ値なら左)を取る、全部取ってから書き戻す |
| クイックソート | 「直後」「3ステップ以内」などステップの並び方 | 右端のピボット、候補の大小、境界の左はピボット以下、ピボットの最終位置 |
| スライダー・チップ | 画面上の何番目か、Mantine の DOM | 名前で取る |
| シャッフル | Math.random の呼び出し回数 | 並びが変わり、本数を保ち、全パネルで同じ |
| オーバーレイ | インラインの style.display | 表示されているか |
| マージの説明・高さ・比較枠 | 再生の何ステップ目か、Fisher–Yates に固定値を与えたときの並び | 表示の順序、高さの基準、完了時の比較枠。必要な並びはシャッフルを繰り返して作る |

UIテストの Math.random は固定の擬似乱数に置き換え、どの実行でも同じ並びで再現できるようにした。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shin4488
shin4488 merged commit 6c7577a into main Oct 26, 2026
5 checks passed
@shin4488
shin4488 deleted the test/spec-based-tests branch October 26, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant