Claude Codeで4つのツールを作った。どれにもテストがなかった。だから直した

Dev.to / 2026/4/16

💬 オピニオンSignals & Early TrendsIdeas & Deep AnalysisTools & Practical Usage

要点

  • 著者はClaude Codeで複数のツールを作ったが、自動テストは用いず、変更が中核の会計ロジックに触れる場合でも手作業での確認に頼っていたと述べています。
  • ERPClawの請求(invoicing)はAIによるリファクタリング後、当初は正しく動作していましたが、後の財務レポートで、請求書の明細行アイテム間で複合税(compound tax)の計算を誤って配分したため、台帳が不均衡になっていたことが判明しました。
  • この出来事は、「動いて合計が正しく見える」だけでも、財務ソフトウェアでは沈黙した(気づきにくい)正確性の不具合が起こり得ること、そして小さな誤りがレポートで発覚するまで見過ごされ、結果的に増幅され得ることを浮き彫りにしました。
  • 複数行の請求書と複合税率に関する単体テストが1つ欠けていれば、回帰(regression)を素早く検知できたはずですが、カバレッジがなかったため、バグの特定まで手作業の追跡に45分かかりました。
  • 著者の経験は、繰り返し現れるパターンを示しています。AI支援によるコーディングは実装を速めますが、プロジェクトの複雑さが増していく中で継続的な信頼性を確保するには、テストの規律(test discipline)が依然として不可欠だということです。

私はERPClawで2時間のClaude Codeセッションを終えたばかりでした。請求書のワークフローがまとまりつつありました。仕訳帳のエントリが生成され、請求書は正しい合計を出し、OpenClawの統合も正しく応答していました。私はターミナルを閉じて考えました。「これが会計側の何かを壊していないか確認すべきだな」と。

しかし、すぐに気づいたのは、確認する手段がないということでした。テストがありませんでした。そもそも、テストが一度もありませんでした。

私はGL(総勘定元帳)の照合ロジックを開いて、しばらく見つめました。Claudeはこのセッションで5つのファイルに触れています。そのどれかが、微妙な何かを混ぜ込んだ可能性がありました。頼れるカバレッジ(保険)がありません。唯一の検証方法は、会計の処理フロー全体を頭の中で手作業で追跡することでした。

私はそれをやりました。45分かかりました。

そのときは、すべて問題ありませんでした。

これは今回が初めてではない

私は同じやり方で、PHPのReddit APIも出荷しました。Claudeは中核となる構造を素早く書き、PSRへの準拠が自然に整い、Laravelのブリッジも初回から動きました。PHPコミュニティがそれを取り上げました。実際の人が使っています。テストはありません。

SiteKitでも同じです。どのプロジェクトでも同じパターンです。Claude Codeがコードを素早く書く→動く→出荷する。そして頭の片隅で「今動いている」ことは「これからも動き続ける」と同じではない、と分かっている。

ERPClawは、それらの中でも最も深刻です。OpenClawプラットフォーム向けのAIネイティブなERPシステムです。会計、請求、在庫、給与計算、税、財務レポーティング――14のドメインにまたがる413のアクション。私が「財務ソフトウェア」と言うとき、それはジャーナルエントリに小さなバグがあっても、何百もの取引に静かに累積し、釣り合うはずのレポートを誰かが実行して初めて「合っていない」と分かるようなソフトのことを指します。

私はそれをテストなしで作り上げました。

私のアプローチを変えたインシデント

ERPClawのセッション中、Claudeが請求書ワークフローのための仕訳帳エントリ作成ロジックをリファクタリングしました。リファクタは妥当でした。コードはきれいに動きました。請求書が生成され、合計も正しく見えました。次の機能に進みました。

さらに2つのセッション後、私は財務レポートを見直していました。台帳がずれていました。複数行の請求書で、複合税率を使うと借方と貸方が釣り合っていません。ほんの少しではあるものの――二重仕訳会計には「だいたい合っていればOK」はありません。台帳は、釣り合うか釣り合わないかのどちらかです。

クラッシュはありませんでした。エラーもありません。数字が静かに間違っていました。

私は手作業で追跡しました。仕訳帳エントリの作成時に、複合税の計算結果を行項目へ配分するのが誤っていました。1つの関数にある、単一のロジックエラーでした。複数行の請求書と複合税率を使うユニットテストがあれば、3秒で見つかったはずです。

代わりに、それを見つけるのに45分の手作業の追跡が必要でした。

数学的に(というより現実として)明らかになりました。ERPClawには413のアクションがあります。つまり、Claudeがコードを書いてから、私が「何かがおかしい」と手作業で気づくまでの間に、静かな回帰(不具合)が潜り込める可能性の窓が413あります。ある時点で、規律(discipline)だけに頼るのは成立しなくなります。

なぜCLAUDE.mdの指示ではこれが解決しないのか

私は分かりやすい修正を試しました。CLAUDE.mdにテストの指示を書いたのです。「ファイルを編集したら必ずテストを書く」。Claudeは時々それに従いましたが、長いセッションでは無視されました。そして、強制する手段はありませんでした。

CLAUDE.mdの指示は助言です。Claudeはセッション開始時にそれを読み、可能な限り最善の形で適用します。しかし、Claudeがアーキテクチャに集中している複数ファイルにまたがる複雑なセッションでは、テスト生成が脱落します。これはClaudeのバグではありません。負荷がかかったときに、注意(attention)ベースのシステムがどう動くかの問題です。

問題は構造的でした。テストには別の意思決定が必要です。誰かが書くと決めなければならない。AIのコーディングは十分に速いので、「このタスクのあとでやる」は「やらない」に等しくなります。

そこで私は tailtest を作った

はっきり見えてからは、修正は単純でした。肝は「ファイルを書き込むイベントそのもの」にフックすることです。Claudeがテストを書くことを決めるのを頼りにしないでください。Claudeがどのファイルを書いたことによっても、テストが自動で起きるようにします。

Claude CodeにはPostToolUseフックがあります。これはツール呼び出しのたびに発火します――ファイル書き込みのたびにも。tailtestはこのフックを使います。

Claudeがファイルを書き込むと:

  1. tailtestが起動します
  2. 知能フィルタ(詳細は下記)を実行します
  3. そのファイルがテストする価値のあるものなら、直前に書かれたコードに対するテストシナリオを生成します
  4. それらのテストを直ちに実行します
  5. すべて通れば:何もありません。静かに続行します。作業を続けます。
  6. 何かが失敗すれば:同じセッションの中で、具体的な出力が返ります。まだ「何が変わったか」を把握できているうちにです。

合格時は黙る――この判断は意図的でした。テストが通るたびにtailtestが何かを話し始めると、1週間もすれば無視するようになります。出力が表面化するのは、本当にあなたの注意が必要になったときだけ。その設計だけが、長期運用で生き残ります。

知能フィルタ

Claudeが書くすべてのファイルが、テストに値するわけではありません。設定ファイルはそうではありません。スキーマ移行もそうではありません。ボイラープレートのインデックスファイルもそうではありません。もしtailtestがそれらすべてで実行されると、うるさく(ノイズになり)、遅くなり、役に立たないテスト出力が大量に生成されます。

tailtestは、何かを生成する前に知能フィルタを実行します。ファイル拡張子、パス、コンテンツのパターンを見て、「テストする価値のあるロジックが含まれているファイルかどうか」を判断します。サービス、ユーティリティ、ドメインモデル、コントローラ、業務ロジック――これらはテスト対象になります。設定、移行、生成物のファイルはスキップされます。

これは任意ではありません。フィルタなしだと、ツールがノイズを生成します。ノイズは、開発者がそれをオフにする原因になります。オフにされたテストツールは何もしません。

ランプアップスキャン

tailtestを既存のプロジェクトにインストールするとします。たとえば私が始めた時点でテストがゼロだったERPClawのようなケースです。このとき、最初のセッションで1000回ものテスト生成を走らせることにはなりません。tailtestは初回実行でコードベースをスキャンし、カバレッジがないファイルを特定して、それらを段階的にバックグラウンドでテストするためのキューに入れます。新しい編集は即座にカバレッジを得ます。既存ファイルは時間をかけてカバーされていきます。

これは実プロジェクトで重要です。スタート直後に「全部を一度にテストする」コールドスタートは役に立ちません。段階的なカバレッジが有効です。

再帰(Recursive)な部分

tailtestには、いまや自分自身のテストスイートで332のテストがあります。そのうちかなりの数は、私がそれを作っている最中にtailtest自身が生成したものです。

ある時点でtailtestが、自身の知能フィルタのロジックのバグを見つけました。私は何かおかしいことに気づいていませんでした。tailtestはファイルに対して実行し、テストを走らせ、フィルタのファイル種別検出をリファクタしている最中に私が入れてしまったエッジケースで失敗を返したのです。

私は「テストツールを出荷するのに正しいタイミング」を考えました。テストツールが自分自身をテストして、出荷する前に自分の回帰(不具合)を見つけるなら、その概念は証明されます。

これは誰のためか

あなたがClaude Codeでコードを書いていて、テストが必要だと分かっているなら: tailtestはその判断を取り除きます。テストは起きます。あなたは覚えておく必要もなく、プロンプトする必要もなく、自分に規律を課す必要もありません。編集のたびにテスト実行が入ります。

あなたがClaude Codeで開発していて、テストが複雑に感じているなら: tailtestがテストを生成します。pytestやvitestの書き方を知る必要はありません。インストールして、そこから先のセッションでは新しいコードがテストされるようになります。失敗が起きたときに分かります。カバレッジを活かすためにテストフレームワークを理解する必要はありません。

正直な限界

返却形式: {"translated": "翻訳されたHTML"}

tailtest はテストを生成します。生成されたテストが正しいテストであることを保証するものではありません。複雑な業務ロジック――ドメインの専門知識を要するような、複数の状態・複数のエンティティにまたがるロジック――については、人間が生成されたテストシナリオをレビューすべきです。tailtest はカバレッジを提供します。テスト戦略に代わるものではありません。

また、トークンコストもあります。すべての PostToolUse 呼び出しでトークンが使用されます。一般的なセッションでは、Claude の利用に約 5 ドル以下が追加されます。実運用のソフトウェアで、本物のユーザーがいる場合――ERPClaw、PHP Reddit API――では、静かな回帰を出荷するコストに比べれば、これは誤差の範囲です。真剣に保守していない趣味プロジェクトなら、これは実際のトレードオフです。私はそれを「違う」とは言いません。

言語とインストール

Python(pytest)、TypeScript および JavaScript(vitest、jest)、Go、Rust、Ruby、Java、PHP。

claude plugin marketplace add avansaber/tailtest
claude plugin install tailtest@avansaber-tailtest

設定ファイルなし。セットアップなし。あなたの言語とテストランナーを自動的に検出します。

出典: https://github.com/avansaber/tailtest
Webサイト: tailtest.com
オープンソース、MIT ライセンス、無料。

質問や課題は GitHub リポジトリにお願いします。私はそれを読みます。