「単体テスト」再入門! 開発の現場でバグを確実に洗い出す最適な手法と、テストケースの作り方

単体テストの定義から手法、未来の展望までを、日本におけるソフトウェアテストの第一人者・高橋寿一さんが解説します。

「単体テスト」再入門! 開発の現場でバグを確実に洗い出す最適な手法と、テストケースの作り方

ソフトウェアのテストにおいて、最初のフェーズである単体テスト。若手Webエンジニアの中には、いきなり単体テストを任されて戸惑った方もいるでしょう。仕方なく現場で踏襲されているやり方に従っているだけ、ということもあるのではないでしょうか?

今回は、単体テストの定義から手法、未来の展望までを、日本におけるソフトウェアテストの第一人者・高橋寿一さんが解説します。

単体テストとは(各社ばらばらな単体テストの定義を再定義)

ソフトウェア開発において、用語の定義は非常に重要です。そこで、単体テストの手法を解説する前に、まずは単体テストの定義を再定義したいと思います。

さて、この「単体テスト」という言葉。会社や人によって意味するところが少しずつ違うな……と思ったことはありませんか? それも仕方ありません。単体テスト(Unit test)の定義が迷走してきた歴史は長く、昨日今日始まったことではないからです。

「単体テスト」という言葉が初めて世に出てくるのは、1970年の『Managing the Development of Large Software Systems』という論文に遡ります{$annotation_1}

次の図でいう「CODING, UNIT TEST(コーディングと単体テスト)」が単体テストになります。

1

しかし日本で仕事をしていると、単機能のテスト(プリントできまっせ、URLジャンプしまっせ等々)も単体テストと言われるケースがあります。

先に書いたように、用語の定義は重要です。開発をスタートする前に、コードに対する確からしさを確認するのを単体テストと呼ぶのか、もしくは単機能に対するテストのことを指すのかを明確にした方がよいでしょう。

コードベースの単体テスト

「コードベースの単体テスト」とはなんだか聞き慣れない用語ですね。でもそういう言い方をここではしていこうと思います。

「単体テストとは何か」という厳密な定義はなく、ISTQBの用語集にもありません。昔はあったような気もしますが、今はありません。それほど曖昧な用語であり、私の参加する世界中の委員会でいつも揉めていました。

そこで本稿では、「コードベースの単体テスト」という用語を使うことにします。この用語を厳密に表現すると、「関数の網羅率を計測し、ロジックの確からしさを確認するテスト」ということになります。これはホワイトボックステスト手法とも言います。この定義に従って話を進めていきます。

単体テストの手法は、機能単位の単体テスト(ブラックボックステスト)と、コードベースの単体テスト(ホワイトボックステスト)の2つに大きく分かれます。コードベースの単体テストは、厳密なソフトウェア品質を求められる自動車や医療のソフトウェアでは必ず行われるテストで、ISO等々で行うことをルール化されています。

さらに、コードベースの単体テストの中には、命令網羅(C0カバレッジ)、分岐網羅(C1カバレッジ)といった、いくつかの手法があります。

ここではよく使われる命令網羅と分岐網羅を紹介します。

命令網羅(C0カバレッジ)

命令網羅テスト、またはC0網羅{$annotation_3}と呼ばれる網羅を説明します。命令網羅でのテスト基準は、少なくとも1回はプログラムの全ての命令文(ステート)を実行することです。例えば、以下のようなコードがあったとします。

if(con1 == 0){
  x = x + 1;
}
if(con2 > 1){
  x = x * 2;
}

この場合、命令網羅テストでは、下記の全ての四角を通る経路をテストします。

2

テストケースは以下のようになります。

con1 = 0, con2 = 2

そうなると赤矢印の部分を通るテストが抜けます。はいそうです。命令網羅というのは不完全なテストなのでやってはいけません。

しかし多くの組織で、命令網羅で単体テストを達成しようとしています。コードの単体テストは労力のかかる仕事ですが、中途半端な命令網羅はせずに、次に紹介する分岐網羅をしっかり行うことをオススメします。

分岐網羅(C1カバレッジ)

分岐網羅またはC1網羅と言われるテストが、上記で示したC0網羅の問題を解決する網羅手法です。分岐網羅では、それぞれの判定条件がTRUE、FALSEの結果を少なくとも1回ずつ持つようテストケースを書きます。

3

条件分岐に比べて網羅性が高いため、ほとんどのケースではこの網羅手法で単体テストを行った方がよいと思います。

先のプログラム例では、次の2つのパターンでデータでテストすれば、テストが完了になります。

テストケース1:con1 = 0, con2 = 2
テストケース2:con1 = 1, con2 = 0

よくある(コードベースの)単体テストの間違い

よくある単体テストの間違いですが、「命令網羅や分岐網羅の網羅率が100%に達すればいいんだ!」と考える人が少なくありません{$annotation_4}

上記のような簡単な例ならいいのですが、一般にソースコードは巨大で、各関数も数百行になり、ある関数からまた別の関数を呼び出している、といったケースもあります。なので、網羅率を100%に持っていくことはなかなか難しいです。

単体テストの目的は、コードロジックの正しさを「網羅」によって論理的に証明することにあります。そしてコードロジックが正しいと証明されれば、それは単体レベルの処理機能のバグがないということを意味します。

ここまでは、命令網羅・分岐網羅というコード経路の網羅手法を紹介してきましたが、「網羅」には、もう1つ別の視点があります。

それが、入力値の網羅です。入力値とは、関数に渡される引数、キーボードから入力される値といった、プログラムに対して与えられるデータです(これに対し、関数の戻り値、画面に出力する値といったデータを出力値といいます)

単体機能のバグをなくすには、分岐網羅の網羅率を100%にするのがコスト面やスケジュール面で難しい場合でも、入力値のパターンを100%網羅し、それに対する期待値(入力値に対するプログラムの仕様通りの出力値)が正しいかチェックする必要があります{$annotation_5}

4

入力値に対する期待処理を確認することが必要

しかし、現場の担当者は往々にして、期待値の確認をメンドウだと言って行わず、コードが網羅されていることで満足しています。そして、単体テストで見つけるべきバグを、次のフェーズである統合テスト6で発見し、品質担保で苦しんでる組織が多いのではないでしょうか?

コードベースの単体テストは、ほとんどのバグを見つけられる(個人的意見では、80%以上のバグを見つけられる)テスト手法です。しっかりテストをすることにより、後工程でのバグの数を著しく減らせます。単体テストでは分岐網羅に、より多くのテスト工数を割くことをオススメしています。

機能単位の単体テスト

ここまでは、コードベースでの単体テストを説明しました。次は「機能単位の単体テスト」を説明していきます。これはブラックボックステスト手法とも呼ばれます。

製品にそれほどの高品質を要求されない場合は、この機能単位の単体テストのみが実施されることが多いです。また、先に説明したコードベースの単体テストを実施したうえ、さらにUIから機能単位(特に複雑な機能)の単体テストを実施する場合もあります。

Webアプリケーションにおいて、一般的には「UIから何かを入力して期待する値が表示される」ことが単体テスト、とシンプルに考えがちです。しかし、複雑な機能についてもバグを見逃さないよう、また無意味なテストケースを膨大に書くことにならないよう、適切なテストケースを抽出することが重要です。

例:複雑なソート機能のテスト

複雑な機能の例として、今回は教科書的なつまらない単体テストの例ではなく、下のような、データをソートする機能確認を想定して解説していきます。

5

この機能では、各列の▽ボタンを押すと、その列をキーに昇順でデータがソートされるものとします。

もし、列内に同じ値がある場合(例えば、名前の列に同性が2人)は、その右隣の列(この場合は年齢)の値によってソートします。

テスト実施者に知らされている情報は、以上の仕様のみです。ブラックボックステストですから、コードのロジックには着目しません。

さて、たぶん初心者は▽ボタンを押して、年齢通りソートできてるな、入社年度ごとにソートできてるな、配置ごとにソートできてるな、といった具合に4つのボタンを押して、「単体機能確認できました!」と報告すると思います。

しかしテストのプロは違います。確実にこの部分からバグを見つけ出すために、最適な数のテストケースを作成していきます。

機能単位の単体テスト手法の基本は、次の2つです。

  • 単機能境界値テスト
  • 組み合わせテスト

境界値テスト

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く