公開日
形だけのテストから、信頼できるテストへ
初日に AI が書いた 159 のテストには、何も確かめていないものが混ざっていた。smoke テストの削除、Slack の偽物の共通化、既知の挙動を固定するテスト、統合テスト、実時間待ちの置き換え、CI での時間切れまで、テストの質を上げていった順に書く。
はじめに
claude-slack-channel の初日のコミットには、159 のテストが付いていた。AI(Claude Code)が実装と一緒に書いたもの。数だけは多いが、中身を読むと expect(true).toBe(true) のように何も確かめていないものが混ざっていた。
テストは、変更のたびに自分で全部読み直す代わりにある。信頼できないテストは、通っても安心できない。この記事は、3 日目から今日までにテストを信頼できる形に変えていった順を書く。
形だけの expect を消す
最初にやったのは引き算。smoke テストのファイルを削除し、expect(true).toBe(true) の 3 か所を、ログに何が記録されたかを確かめる検証に置き換えた。
同時に TypeScript の厳格オプションを足し、テストも型検査の対象にした。テストは本体と同じ基準で型を通す。テストだけ緩いと、本体の型を変えたときにテストが古いまま残る。
Slack の偽物を 1 か所にまとめる
Slack の Web API と Socket Mode の偽物は、テストファイルごとに書かれていた。これを test/helpers/fake-slack.ts に切り出し、どのテストからも同じものを使う形にした。
偽物が複数あると、偽物ごとに挙動が少しずつ違い、どれが本物に近いのか分からなくなるんだよな。1 か所にまとめれば、Slack の仕様で分かったことをそこに反映すればよい。
既知の挙動を現状のまま固定する
リファクタリングの前に、今の挙動をそのまま固定するテストを足した。正しいかどうかは問わない。yes maybe のような文が回答として扱われる、添付の件数の表記、ロックの失効の境界など、今こう動いているという事実を書く。
長文の分割は、分割前の実装をそのままテスト用のファイルに複製し、固定シードで 2,400 ケースの入力に対して新旧の出力が一致することを確かめた。これで分割の関数を 5 つに分ける変更ができた。
固定したテストは、方針を変えるときに更新する。yes maybe は後で通常のメッセージとして届ける方針に変えたが、そのときコミットメッセージに「現状固定テストの変更」として、どのテストの期待値をなぜ変えたかを書いた。テストの変更に理由が付いていれば、挙動の変更が意図したものだと後からちゃんと分かる。
統合テスト
部品のテストだけでは、配線の間違いが残る。偽物の Slack とインメモリの MCP クライアントで、実物のブリッジを起動し、Slack のイベントの受信から Claude への通知、ツールの呼び出し、Slack への投稿までを通すテストを足した。
このために、エントリポイントから配線の部分を別のファイルに分けた。配線を関数にしておくと、テストから起動と停止ができる。import しただけでプロセスのリスナーや状態ディレクトリに副作用が出ないことも、ここで確かめている。
その後の修正では、「修正前のコードでは落ちる」テストを先に書くようにした。伏せ字の漏れ、再起動フラグの消し忘れ、別のコンソールの画面の読み取りなど、コミットメッセージに修正前に何件落ちるかを書いている。
実時間待ちを vi.waitFor に替える(#18)
hook の結合テストは、20 ミリ秒ごとの読み取りを 60〜80 ミリ秒の実時間で待っていた。CI の負荷次第で流石に不安定になる。しかも「投稿が無い」ことを確かめる側は、読み取りの前に判定しても通ってしまう。
投稿を待つ側は vi.waitFor で条件が満たされるまで待つ形にした。投稿が無いことを確かめる側は、必ず知らせる目印の行を書き足し、それが届いてから判定する。残した実時間待ちは、時間の経過そのものを確かめる 2 件だけ。
CI と PowerShell の時間切れ(#25)
GitHub Actions で typecheck・テスト・lint を回すようにした。本番は Windows だけなので windows-latest で PowerShell 絡みのテストも走らせ、ubuntu-latest ではパス処理の環境差を見る。
最初の実行で、PowerShell のスクリプトを実際に起動するテストが時間切れになった。手元では 5 秒で十分だったが、CI では初回の起動がそれを超える。時間切れを 30 秒に延ばして通した。手元で通ったつもりの崩れに早く気付くのが CI の目的なので、最初の崩れがこれだったのは、まぁ順当じゃないか。
実機で確かめるもの
それでも自動化できないものは残る。警告ダイアログへの自動応答や、複数のキーが届くかは、実機で確かめるしかない。開発のドキュメントに、単体・結合・実機の 3 段階を書き、実機で確かめる項目を列挙している。
テストが確かめていることと、確かめていないことを分けて書いておく。それが今のところ、AI が書いたテストと付き合う一番の方法。
次回は、2 つの AI モデルで相互にレビューし、指摘を Issue で回した話。