単体テストがうまくいく(または失敗する)10の方法

この記事はCameron Lairdによるゲスト投稿です。

単体テストの威力は絶大です。単体テストは大きな価値をもたらしてくれます。いっぽうで、間違った方法で行えば、相当の害もあります。

この記事では、プロジェクトでよく見かけるが誤解されていることも多い、単体テストに関する10の状況を挙げ、単体テストがどのように役立つか、あるいは問題になるかを判断できるようにします。

1.発見に役立つテスト

広範囲にわたる単体テストで私が気に入っているところの1つは、何か間違いがあればすぐに驚くような結果が出ることです。最近、ある計算アルゴリズムに手を入れていたとき、まったく関係のないコンテンツ管理部分のエラーメッセージがおかしくなりました。結局は、1週間ほど前にほかの誰かが、文字列のディープコピーを行うべきところで、文字列の結果自体を操作するよう記述していたことがわかりました。

私が作業しているときに問題が発見できて、どれほどほっとしたでしょう。もし私が新しく実装したところの周辺で数値結果だけをテストしていたなら、ディープコピーが行われていなかった問題は、あと数週間は隠れたままで、発覚したときには、真の意図や修正方法を突き止めるのがもっと難しくなっていたかもしれません。

私たちの単体テストは広範囲かつ軽量なので、1時間のうちに何回も実行して、アプリケーションの全体的な健康状態について役に立つ測定値を得るのは容易です。もちろん、単体テストは正しさを保証しませんが、私が知らぬ間に災難にはまり込むのを防ぐのに大いに役立っています。

2.実行されないテスト

単体テストの最悪の失敗の1つは、まったく実行されないことです。時間やセットアップの難しさやそのほかの特性のためにコストが高く、実行に特別な努力のいるテストは放棄されがちです。

私たちはみなテストを自動化したいと考えていますが、自動化の本当の価値は、手間を省くことよりは、一貫性にあります。たとえ午後6時以降でも、特定のオフィスにだけ適用される祝日でも、あるいはそのほかの単独では些細だが現実の商習慣においてリスクを増やすさまざまな条件が重なった場合にでも、自動化された単体テストはより正確に実行される可能性が高いでしょう。

3.無視されるテスト

おそらく、実行されない単体テストより悪いのが無視される単体テストではないでしょうか。あまりに多くのプロジェクトが、複雑な関係を完全に把握しているエキスパートにしか管理できない大規模なテストスイートを抱えています。「タイミングのずれの問題を合格にして、スペルに関する指摘があればそれも合格にして、それから…」等。目標はエラーを出さずにテストを実行することにあり、そのほかのことに対処する余裕はないのです。

プロジェクトは確かに優先順位を付ける必要があります。ときには何千もの誤検出が発生する可能性があり、うまくいっているプロジェクトは取捨選択を行っているものです。そういった選択の一部は、継続的テスト全体の機能を保ち、マスターまたは参照ソースが警告なしでテストに合格するために必要とされます。

どういったエラーが注目に価するかという、誤りを犯しやすい議論を繰り返すよりも、特定のテストを一時的かつ明示的にオフに設定するほうがはるかにましです。単体テストはエラーなく実行されるものと認識し、その状態を保ちましょう。

4.統一されたスタイルを保証するテスト

先ほど、私がときに何千ものエラーが検出されると言ったのは本気だったでしょうか?もちろん本気です。チームがより多くのスタイルの選択を自動化したときなどにそういったことが起こる可能性があります。

たとえば、タブかスペースかという選択をモラルに訴えたり、感情的なコミットレビューや独立した品質保証チームにまかせるのではなく、自動ツールを利用した場合です。初回の実行で検出される逸脱の数にはショックを受けるかもしれません。それでも、自動化は疑問に恒久的な決着をつけるため、最後までやり通す価値があります。

メソッド定義にコメントヘッダーがあったほうがよいでしょうか?すべてのプログラマーはインデントの幅を同じにするべきでしょうか?参照されていない変数を保持する理由があるでしょうか?適切なツールを使用してこのような疑問を恒久的に解決し、ソースのスタイル品質を高く保ちましょう。

5.価値が少ないテスト

実行されないテストや無視されるテストは役に立ちません。しかし、実行する価値がないテストもあります。テストが提供する情報よりテストを維持する手間の方が大きい場合です。テストにこのカテゴリにあてはまるものがあるなら、削除することでテストスイートを改善しましょう。

6.エラーを扱うテスト

あなたの単体テストスイートはエラーパスを実行していますか?エラーパスを実行するテストを追加することは、多くのアプリケーションでテスト品質を容易に改善できる方法です。

テストがハッピーパスに偏っていることがよくあります。エラー処理の明示的なテストが不足していると、必然的に、エラーをより注意深く検証したときに予期しない問題が見つかります。さもなければ、簡単にエンドユーザーが「関数でエラー errno が発生しました」あるいは「メッセージに必須の [プレースホルダー] が含まれていません」といったメッセージを目にすることになりかねません。

他の要件と同様にエラー処理も要件です。失敗するテストを作成するとき、あなたは単体テストにエラー処理を真剣に扱うことを教えているのです。

7.遅すぎるテスト

先ほど、実行が速いので単体テストを気に入っている、実行が速いことが頻繁な実行につながると書きました。では、単体テストの実行がどうしても遅い場合はどうでしょうか?モジュール化、最適化、高度なモックの使用、そのほかのあらゆる努力をもってしても、依然としてテストの完了がワークフローに適した数秒ではなく何時間もかかる場合はどうしたらよいのでしょうか?

その場合、テストを何層かにセグメント化します。高速なテストはインタラクティブなレスポンスを得るために残し、より時間がかかるテストも確実に定期的に実行されるようなフレームワークを構築します。時間のかかるテストを放棄してはいけません。ふさわしい場所を作ればよいのです。

8.見せかけをあばくテスト

単体テストの現実を見ましょう。たとえば、高いカバレッジは誇ってもよい点であると同時に、何の保証でもありません。

テストスイートのカバレッジが100%でも、コードの誤りが隠れている可能性は多くあります。

  • sum = first * secondという行はカバーされているかもしれませんが、first = 2 およびsecond = 2でしかテストされていない場合、結果もあてになりません。加算と乗算の取り違いは不可解でめったにないエラーに見えるかもしれません。しかし、トータルでは不可解なエラーは多数あります。
  • 2つの変数のすべての値を検証するテストが、必ずしも変数のすべての組み合わせをテストするとは限りません。
  • 値には認識されているよりも広い範囲があるかもしれません。Unicode、タイムゾーンの動作、色の組み合わせ、そのほかの特殊な演算領域のテストが、結局は意図したような範囲全体ではなく値のサブセットしかカバーしていないとわかることはよくあります。値に対する動作だけではなく、値範囲も特殊な知識であるかもしれません。

単体テストは価値がありますが、支援が必要です。この課題に対処しましょう。エラーがすべてのテストをすり抜けて、エンドユーザーからの報告で初めてわかったとしたら、アプリケーションや組織はどのように応答しますか?それはあなたがそのような状況で受け取りたい応答でしょうか?そんなことが起こる前にシステムを整備しましょう。

9.正しいが役に立つとは限らないテスト

言明された要件をすべて満たしているアプリケーションでも利便性、アクセシビリティ、そのほかの単体テスト作成時に誰も検討しなかった品質が低いということはあり得ます。これには簡単なソリューションはないかもしれませんが、可能性を意識することは役に立ちます。

10.依存関係の予期しない問題を検出するテスト

あなたがちょっとした拡張のコーディングを始めたとします。最初の継続的テストのレポートには、まったく関係のない部分のエラーが指摘されていました。なぜそんなことが起きるのでしょうか?

可能性はいろいろあります。

  • エラーが出た部分は、現在作業している部分と思った以上に深いつながりがあるかもしれません。
  • あるいは、オリジン—変更を加える前のソース—がすでに壊れていたのかもしれません。
  • あなたの制御できない何か、ひょっとするとシステム全体の外にある何かが変わって不整合が生じたのかもしれません。

おそらくは、あなたのプロジェクトはほかに同じものはないでしょう。外部ライブラリへの依存関係の組み合わせは、他のどんなアプリケーションとも異なるでしょう。それらのライブラリがすべて品質が高く、それぞれのリリース前に注意深くテストされているとしても、あなたがテストしたときに他の誰も遭遇しなかった現象が発生することはあり得ます。

あなたがそうした現象に遭遇したら、顧客が操作したとき初めて見つかるのではなく、プログラミング中にいち早く見つかったことを喜びましょう。

まとめ

コンピューター処理には大きな柔軟性があり、それに対応してシステムのテストも広く深いものがあります。単体テストの基本的な概念は単純ですが、単体テストを最大限に活用するには、細部に注意を向け、さまざまな個々の状況に合わせて調整する必要があります。ここで紹介した10の状況に気を付けて、どれを真似し、どれを避けるべきかを知ってください。

Cameron Lairdは、賞を授けられたソフトウェア開発者で著述家です。Cameronは、Python Software Foundationの投票権を持つほか、いくつかの業界のサポートや標準化組織に参加しています。長くTexas Gulf Coastに住み、農場自動化アプリケーションがお気に入りです。

(この記事は、開発元Gurock社の Blog 「10 Ways Unit Tests Go Right — and Wrong」2020年12月8日の翻訳記事です。)

eBook 公開中

Paul Gerrard著 効果的なテスト管理12の秘密 (日本語)

テスト計画やテスト管理に役立つ12のトピックを解説します。

詳細はこちら