この記事は、連載「確かめない技術」の第 5 回です。うまくいかない症状と、その対処を一つずつ並べています。各回はファイルやスクリプトを一つ置けば完結します。その仕組みがなぜ要るのかは、置いたあとに解説を読めば分かります。連載の全体像と各回の一覧は序論にあります。

今回は、テストを間引くことの話です。間引くというのは、重いテストを普段の実行から外しておいて、そのテストが確かめる領域のコードを触った日にだけ走らせることです。私はある日、AI から「テストは通っています」という報告を受け取りました。そのとき走らなかったテストがどれだけあるかは、報告のどこにも書かれていませんでした。これが、速くする工夫は死角を隠すという問題です。間引いたぶんのテストは、報告からも消えます。

1 作目「読まない技術」の第 1 回で、私は、ラッパー(テストを走らせるコマンドを包むスクリプト scripts/test.sh)を置き、テストの出力を、件数と失敗だけの数行の要約にしました。今回は、その要約に、走らせていない領域を書き足します。

まず一つだけ、確かめてみてください。

直近のセッションで、AI が「テストは通っています」と報告した場面を探してください。

その報告に、そのとき走らせなかったテストのことは書いてありましたか。

重いテストを普段の実行から外すこと自体は、正しい判断です。実画像を書き出す画像処理のテスト、閾値まで実リクエストを繰り返すレート制限のテスト、26 万行のデータを投入する精度検証のテストなどは、そのテストが確かめるコードを触った日にだけ走らせれば足ります。

あなたの環境では、ラッパーが、走らせていない領域の一覧を、テストの要約の末尾に毎回出しているとします。AI が全量を走らせて、「テストは通っています」と報告してきました。AI は、走らせていない領域のことを報告に書いていませんでした。あとで、走らせていない領域の 1 つに、本物の失敗が 1 件あったと分かりました。このとき、あなたなら次のどちらを選びますか。

  • 一覧を要約の先頭に移して、AI が見落とさないようにする
  • 走らせていない領域の数を、AI の報告を通さずに、自分の画面に直接出す

答えは、自分の画面に直接出すほうです。一覧が報告から消えたのは、報告に何を書くかをAIが決めるからです。私の手元では、一覧が要約に毎回出ていたのに、AI が全量を 8 回走らせて、一覧が報告に載ったのは 1 回でした。一覧を要約のどこへ動かしても、報告を書くのはAIのままです。数をAIの報告を通さずに画面へ出せば、AI が報告に何を書いても、走らせていない領域があることは目の前に残ります。

理解したことは一文にできます。

テストを間引いて速くすると、どのテストを走らせていないかが、AI の報告から消える。だから、走らせていない領域の数は、AI の報告を通さずに、テストを走らせるたびに自分の画面へ直接出す。

CC BY 4.0

仕組み: 走らせていない領域を、一覧と数の 1 行で毎回出す

この仕組みを作るのは、三つの変更です。一つ目に、重いテストに領域名を付け、その領域名を指定したときだけ走るようにします。領域名を指定するのは、テストを走らせるときの環境変数 E2E です。

# 重いテストの側(例: 決済フローの E2E)
setup do
  skip "runs only with E2E=payment" unless ENV["E2E"].to_s.split(",").include?("payment")
end

二つ目に、ラッパーが、テストを走らせるたびに、要約の末尾に、走らせていない領域の一覧を出します。三つ目に、ラッパーは、その下に、走らせていない領域の数を、決まった書き出し([observed])の 1 行で出します。

# scripts/test.sh の要約の最後に足す(短くしたもの: 単一指定を想定。手元のコードは複数指定と領域の説明に対応)
not_run=$(grep -rhoE 'E2E=[a-z_]+' test/ | sed 's/E2E=//' | sort -u | grep -vx "${E2E:-__none__}")
echo "[E2E] areas run: ${E2E:-none}"
echo "[E2E] the following areas were not run. Re-run with E2E='<area>' only when you touched them:"
printf '%s\n' "$not_run" | sed 's/^/  - /'
echo "[observed] E2E: areas not run $(printf '%s\n' "$not_run" | grep -c .)"

私の環境では、要約の末尾は次の形です。

[E2E] areas run: payment
[E2E] the following areas were not run. Re-run with E2E='<area>' only when you touched them:
  - thumbnail generation (writes real images)
  - rate limiting (repeats real requests up to the threshold)
  - score posting → propagation to aggregates
  - batch idempotency (run twice, same values)
  …

私の環境では、領域名を指定せずにテストを走らせるたびに、この一覧に 22 個の領域が 1 行ずつ並びます。

一覧は、AI が走らせ直すかを決めるためのものです。決済のコードを触ったあと、AI が普段どおりテストを走らせると、決済の重いテストはスキップされ、一覧に、payment が載ります。一覧の見出しにあるのは、領域名を指定して走らせ直す指示(Re-run with E2E='<area>')です。AI が要約を絞らずに受け取れば、報告を書く前に、E2E=payment を付けて決済の重いテストだけを走らせ直すことができます。上の出力は、この走らせ直しのあとの要約で、一覧から、payment が消えています。

数の行は、AI の報告を通さずに私の画面へ出すための行です。[observed] で始まる行を拾うのは、1 作目 第 10 回で置いた hook です。この hook は、AI の最終応答にこの行がそのまま入っていなければ、hook 自身がその行を私の画面に出し、AI が出力を | grep で絞って受け取ったときも、ラッパーが残したログからこの行を拾います。AI が報告を、全量が通ったという形に畳んでも、その下に、走らせていない領域の数が出ます。

CC BY 4.0 はここまで

走らせなくなったもの

走らせなくなったのは、安心のための全量実行です。「何もしていないけど、全部流して合格を見ておくか」と考えて全量を走らせることを、私はやめました。安心の根拠は、合格の総数から、「走らせていない 22 個の領域のどれにも今日は触っていない」という一覧との照合に変わりました。

この照合は、一覧がテストのたびに要約の末尾に出るので、数秒で終わります。

CC BY 4.0

注意: 「触ったのに走らせ忘れる」は残る

この一覧と数の行がするのは、走らせ忘れを見えるようにすることまでです。決済を触った日に、AI が E2E=payment を付け忘れることはあり得ます。その走らせ忘れに気づけるのは、一覧に、payment が載っているのを、AI か私が読んだときです。

検証手順: 領域名を指定しない実行と、指定した実行を一度ずつ

前提

重いテストを 1 つ以上選んで領域名を付け、その領域名が指定されたときだけ走る形にしておきます。ラッパーの要約の末尾には、走らせていない領域の一覧を出す処理を足し終えておきます。

所要時間: 10 分

手順

  1. あなたが、普段どおりに(領域名を指定せずに)テストを走らせます
  2. あなたが、領域名を指定してテストを走らせます(E2E=<領域名> のように)

合格条件(すべて満たすこと)

合格の条件は 3 つです。手順 1 では、要約の末尾の一覧に、その領域名が載っていることを確かめます。手順 2 では、その重いテストが実際に走り(要約の件数が手順 1 より増えます)、末尾の一覧から、その領域名が消えていることを確かめます。

合格しなかったとき

  • 一覧が空のまま変わらない —— ラッパーが、テストコードから領域名を拾うことができていません。一覧の中身は「指定した領域を引いた残り」なので、空の一覧は、「全部走った」より「拾うことができていない」の可能性のほうが高いと思ってください

後始末

要りません。この検証は何も壊しません。

この検証で確かめたのは、走らせていない領域が一覧に出ることだけで、走らせ忘れを防ぐことは別です。


CC BY 4.0 はここまで

次回は最終回、「その場で決めない技術」です。