はじめて学ぶソフトウェアのテスト技法を読んだ

フィヨルドブートキャンプのプラクティスには、自分で作成したRailsアプリのテストコードを書くプラクティスがあります。 実際にテストコードを書く前に、どのようなテスト技法があるか、TDDとは何か、test-unitについて学習します。 テスト技法について勉強するために、はじめて学ぶソフトウェアのテスト技法を読んだので、まとめていきたいと思います。

www.amazon.co.jp

どのようなテストの種類があるのか?

ブラックボックステスト

用件や仕様に基づいて行うテスト。プログラミング詳細の知識がなくてもテストできる。 ブラックボックステストを行うには仕様から期待する値、期待しない値を選択し、それぞれの入力に対する期待する値を決める。 仕様からサブセットの組み合わせを決定することで、総当たりで入力するデータを組みわせて検証しなくても良い。

ホワイトボックステスト

ソフトウェアの実装、構造に基づいて行うテスト。プログラミングの知識が必要となる。

目指すべきテストケースとは?

全ての可能性を予想してそれらをテストすることは不可能。 最小の時間と労力でほとんどのエラーを検出できるテストケースを目指す。

テストのレベル

単体テスト

ソフトウェアの最小単位をテストする。 何を最小単位とするかはプログラミング言語によって異なる。

統合テスト

単体テストでテストした複数のプログラムを統合させた時に動くかどうかテストする。

システムテスト

もっとも高いレベルの統合時(ソフトウェアが完成した時)に起きる欠陥に焦点を当ててテストする。

受け入れテスト

発注者がソフトウェアを受け入れて、ベンダーに代金を支払うか検証するテスト。 発注者の意図通りに動くか確かめる。

ブラックボックステスト

同値レベルテスト

以下のようなコードがあったとする

def sake
  age = ARGV[0].to_i
  if age < 20
    puts '飲んじゃダメ!'
  else
   puts '飲んでいいよ'
  end
end

sake

$ ruby sake.rb 20
飲んでいいよ
$ ruby sake.rb 10
飲んじゃダメ!

このコードが正しく動くテストを書く場合、n~19までの値全てをテストする必要はない。 この範囲のテストに必要なのは20未満の値ひとつのみ。12を選んでも1を選んでもテストという観点から見れば同じ値である。 ここでいうテストの範囲のこと「同値クラス」と呼ぶ。 「同値クラス」を使ったテストを書くことで、テストケースの数を減らすことができる。

同値クラステストを行うときは、1回に1つの無効値をテストするようにする。1つの無効値に絞ることで、システムが正しく無効値を認識したか確認することができる。

異常値が引数に渡されるパターンもテストするべきなのだろうか? 答えはインターフェースの事前条件によって異なる。 事前条件とはモジュール、メソッドが正常に動作するために期待される条件のこと。 ちなみに事後条件とはメソッド、モジュールが何をするかということ。 事前条件で受け付けるデータがメソッド、モジュール外でしっかり定義されている場合、異常値のテストは実行しなくても問題ない。このようなテストのことを「契約によるテスト」と呼ぶ。 逆にどんな入力でも受け付けるメソッド、モジュールの場合は異常値のテストも実行しなければならない。 上記のプログラムを例にとって考えてみる。 メソッドsake内のage変数はコマンドラインから入力された値を代入しており、どんな入力でも受け付ける状態。 なのでsakeメソッドには異常値が入力された場合の処理、テストを書かないといけない。

境界値テスト

  • データの境界に注目して行うテスト
  • 境界値、境界値のすぐ下、すぐ上のデータをテストする
  • 比較演算子の打ち間違いなどにより境界は欠陥ができやすい

デシジョンテーブルテスト

デシジョンテーブルは、条件の組み合わせに基づいて、条件と対応するアクションを表形式で表したもの。 抜け漏れなく全ての組み合わせをテーブルに格納する必要がある。 テストだけでなく複雑な仕様を確認するのに有効活用できる。

ペア構成テスト

全ての変数の全て値の全ての組み合わせを検証するのではなく、全てのペアの組み合わせを検証する。 ソフトウェアの欠陥のほとんどはシングルモード欠陥、ダブルモード欠陥のいずれかに分類される。 ペア構成テストはシングルモード欠陥、ダブルモード欠陥をカバーする最小の組み合わせを提供する。 直交表か全ペア方式を使ってペアを作成する。

  • 直交表  数字の入った二次元の配列で配列から任意の2列を選ぶと、別のペアをどう選んでも値の全てのペアの組み合わせができる。 qiita.com

  • 全ペア方式  macの場合以下の記事が参考になった。 qiita.com

ペア構成テストはあくまで「全てのペア」の組み合わせをテストするので、シングルモード欠陥、ダブルモード欠陥以外の欠陥はカバーできない。よって特殊な条件がある場合テストを追加する必要がある。

状態遷移テスト

  • 状態遷移図を書くことによって、状態、次の動作、イベントを明確にできる。
  • 状態遷移図と合わせて状態遷移表を使うことで抜け漏れなく遷移を確認できる。

ドメイン分析テスト

  • 複数の変数を同時にテストするための技法。
  • 境界値が不正確に定義、実装された条件を見つけることができる。

ユースケーステスト

  • アクターがどうシステムを使うのシナリオのこと。アクターはユーザーを指すことが多い。シナリオとはユーザーとシステムの相互作用のこと。
  • ユースケーステストはユーザーの視点から定義される。システムの視点からではない。
  • 主成功シナリオに対して少なくとも1つのテストを書く。

ホワイトボックステスト

制御フローテスト

  • モジュール内の実行パスを識別して、それらのパスを網羅するテストを作成する。
  • 制御フローグラフを元にパスを分析する。

データフローテスト

  • 最初に値を代入せずに参照するミスを確認するためデータフローテストを行う。
  • モジュール内の実行パスを識別した後、各パスの使用、未定義の変数のペアをテストする。

テストのパラダイム

スクリプトテスト

  • ソフトウェア開発手法の一つのウォーターフォールモデルの一部分として世に登場した。
  • スクリプトテストとは公式なシステム要求に基づいてテストを計画すること。
  • 再現性、監査性、客観性を担保できる。

探索的テスト

  • 探索的テストとは製品を開発しながらテストの設計と実行を行うこと。
  • 実行すべきてテストケースが事前に決定できず、一つ前のテストの結果を元に次のテストを考えなければならない場合、探索的テストを選択する。

テストの計画

  • スクリプトテストを採用したからといって、探索的テストを取り入れてはいけないということではない。逆も然り。
  • 計画が現在進行形のプロセスである場合、現在の計画は暫定的なものであることを認め、新しい知識や情報が入った時は計画を見直すべきである。

支援方法

欠陥の分類

  • 欠陥を分類することでテストの方向性、重要度を決めることができる。
  • 分類せずにテストすることもできる。

テストの終了判定

テストを終了するか決める時の5つの基準

  • 事前に決めたカバレッジの目標値を達成した
  • 欠陥検出率が、事前に決めた基準以下に下がった
  • 次の欠陥を見つけるのに要する限界コストが、その欠陥で生じると予想される損害額より大きくなった
  • プロジェクトチームが、製品をリリースしても良いという意見に達した
  • 上司による「いいから出荷しろ」の一言

感想

まだまともにテストコードを書いたことがないのでなんのこっちゃと感じる内容が多かったですが、新しいことを学んでいる時によく発生する現象だと割り切って読み進めました。 ひとつ残念な点を挙げるとしたら、章末問題の答えが載っていないことです。 本書を読んで一番よかったと思う点は、正しいテストとの向き合い方を学べたことです。 闇雲、気まぐれにテストを書くのではなく、「最小の時間と労力でほとんどのエラーを検出できるテスト」をかけるエンジニアを目指したいと思いました。 そしてこの本には「最小の時間と労力でほとんどのエラーを検出できるテスト」を書くためのテクニックが詰まっています。 どんなテストを書けばいいんだ?と迷った時、このテストは何のためにあるんだろうと疑問に思った時、読み返したい一冊です。