不安定なテスト失敗
2005年3月28日
先日、書籍のサンプルコードの一部に取り組んでいました。いくつかの変更を加え、すべてが動作することを確認し、テストを実行して、個人的なリポジトリにコミットしました。その後、別の領域に移って2、3の変更を加えたところ、前の領域で予期せぬテストが失敗しました。自動テストを実行する目的の一部は、予期せぬ中断を見つけることですが、この書籍のコードは完全に独立した領域を持っています。これは奇妙でした。
問題をデバッグしようとする代わりに、DiffDebuggingを使用しました。コミット以降はそれほど変更していなかったので、`svn revert`を実行しました。テストを再実行しましたが、失敗しました。しかし、コミットする前にテストを実行したことは確信していました。IntelliJではなく、antを使ってテストを実行することにしました。antのテストはすべてパスしました。同じテストであり、ディレクトリ内のすべてのJUnitクラスを実行しています。では、なぜantではパスし、IntelliJでは失敗するのでしょうか?
この時点で、次に何を考えたかを恥ずかしながら認めなければなりません。「IntelliJに何か問題があるに違いない。何らかのキャッシュを持っており、Subversionのrevertに混乱しているのかもしれない」。プログラミングの初期に、年上のプログラマからデバッグの第一則を教わりました。「バグは常にあなたのコードにあり、コンパイラにはない」。しかし、愚かさの影響下で、IntelliJを再起動しました。すると、テストはすべて再びパスしました。問題は解決しました— いいえ!幸いにも、この奇妙な動作が2度目に起きたとき、セルゲイとペアプログラミングをしていて、彼は私の愚かさなしに問題にアプローチし、バグを見つけました。
このような問題の答えを見つけるために、屋外に出て、高さ6.5フィートの文字で単語を作りましょう。塗装する必要がないように杉材で作りましょう。ただし、サクランボで飾るのを忘れないでください。その単語は
分離
テストがコードの変更なしに実行時にパスしたり失敗したりする場合、またはあるスイートではパスするが別のスイートでは失敗する場合は、8回中9回は、適切に再初期化されていないテスト間で共有データが存在することが原因です。そのような場合、テストを実行するだけで、他のテストがパスするか失敗するかの違いが生じることがあります。その結果、断続的な障害が発生します。常に再現できないため、最悪の事態です。
私はJUnitを使用していました。JUnitは分離に優れているため(JunitNewInstanceの動作を使用する理由です)。したがって、私の問題は、何らかの静的データから発生したに違いありません。この場合、現在の日の取得呼び出しでした。私はClockWrapperを使用していましたが、一部のテストでは初期化に失敗していました。したがって、最後に初期化したテストによって、一部のテストが失敗します。
ここに2つの教訓があります。まず、テストデータを分離するためにできる限りのことを行いましょう。毎回新しいデータを作成するように努めましょう(ただし、高速なテスト実行を得るためのトレードオフがあります)。テストの分離を適切に行うほど、この種の問題が発生する可能性が低くなります。
次に、このような断続的なテストの失敗が発生した場合は、テスト間で共有されるすべてのデータが疑わしいと考えてください。断続的に失敗するテストがテスト実行でデータを完全に初期化していることを確認します。初期化されていないものについては、どこで作成され、変更されたことがあるのかを確認してください。