はじめに
WEBアプリ開発で実施するテストについて、学習する機会がありましたのでまとめてみました。
業務でテストケースを書き起こす機会は多々あるものの、あんまり勉強してこなかった人のチラシの裏です。
[読んだ本]
知識ゼロから学ぶ ソフトウェアテスト【改訂版】
テストにもいろいろある
テストには大きくわけて、2つの技法があると言われています。ホワイトボックステストとブラックボックステストです。WEB開発現場で、実施する範囲で最も多いのが、単体テスト、結合テストでしょう。現場によって若干の差はあるものの、多くの場合、単体テストはホワイトボックス、結合テストはブラックボックスに当たります。
WEBアプリではホワイトボックステストは、意味がないとは言いませんが、時間がなかったり規模的に実施が現実的ではないなど、さまざまな理由でブラックボックステストを中心に実施されていることと思います。
とはいえ、ブラックボックステストもホワイトボックステストの延長です(受け売り)。両方おさらいしていきます。
ホワイトボックステスト
コードの中身の論理構造が正しいかを確認するようなテストのことを指します。「論理構造しか確認しないため、仕様的に正しいかは確認しない」というのがポイントです。
「制御パステスト」という手法が用いられます。これから列挙するテスト手法は、それを細分化したものになります。フローチャートの分岐チェック的なテストです。カバレッジテストというやつです。
※カバレッジについては、以下のページがわかりやすかったので、ご参照ください。
ステートメントカバレッジ
命令文を1回は実行するテストです。
if(a > 5){
command1();
}
if(a == 5){
command2();
}
このようなコードがあった時、commnand1とcommand2が必ず実行されるようにテストを実施します。
ただ、今回の場合、命令文として「5以下」のケースが存在しません。そのため、「5以下」ケースは漏れますし、条件が誤っていた場合にバグが見つけられない場合もあります。命令文を1回実行すれば良い、というテストなのでこのようなことが発生します。
意味がないわけではないけれど、テストとしては弱めとのことです。
ブランチカバレッジ
分岐をチェックしていくテストです。分岐コードに対して、それぞれの判定条件がTrue、Falseの結果を少なくとも1回ずつ持つようにテストケースを書いていきます。
このテストでは2点ほど注意すべきことがあります。
- 注意点1
-
ステートメントテストで書いたような分岐なら大したことはありませんが、通常の業務で見かけるような処理では、ケース量がだいぶ多くなるので、全部に対してテストするのは厳しいでしょう。代わりに、バグの状況を見て(※)、バグが出てる部分に大してカバレッジを実行するのも1つの手とのことです。
※バグの出るコードは偏るんだそうです。 - 注意点2
- カバーできないコードがあります。分岐を通ることしか目的にしていないため、エラー処理、デッドコードなどはカバーできません。
カバレッジテストの全体の注意点
以下のバグは、カバレッジテストでは検出できない(しづらい)場合があります。
ループに関するバグ
参考書では、開発者≠テスターの想定で、「どこがループしてるかわからないし、ループ処理してるところってだいたいは処理が複雑よね。」という前提で、認識しておらず検出できないことがある、とされていました。が、開発者=テスターでもうっかりすると見逃すこともあるでしょう。
対策として、以下のようなパターンを用意しておくことで、検出しやすくなるそうです。
①ループなし(スキップする場合)
②ループ回数が1回の場合
③ありがちな回数
④最大ループ回数より1回少ない
⑤最大ループ回数
⑥最大ループ回数+1回
要求仕様が間違ってる、または機能がない
これはカバレッジテストに限りませんが、要求仕様に沿ってるか見るのがテストなので、これが違ったらバグの発見は無理ですね。あきらめましょう。
データのバグ
WEBアプリでは多くの場合、DBでデータを扱ったり、外部から渡されてきたデータを処理します。カバレッジテストの段階では、データが適切に処理されているか、ということは意識できません。
例えばスレッドで並列処理をしている時に、それぞれのデータが正しく処理されているか、使っているデータが間違っていないか、などはカバーできないということです。カバレッジテストは論理構造の確認しか目的としないからです。
そのため、データの処理についてのバグは、ブラックボックステストなど別のテスト手法で検出する必要があります。
マルチタスク等のタイミングに関するバグ
これは残念ながら、以下のようなパターンで対症療法的にがんばるしかないそうです。
①データがプロセス・スレッド間で共有されている場合、そのデータへのアクセスパターンに応じたテストケースを作る。
②すべてのプロセス・スレッドについて、生成と消滅の組み合わせをテストする。
③できるだけたくさん、プロセス・スレッドを立ち上げてテストする。
ツールなどを使いながらがんばりましょう。
ホワイトボックステストおまけ
TDD<Test Driven Development(テスト駆動型開発)>
これもホワイトボックステストにあたるそうです。メソッド単位で正しいかを、ロジックが通るかどうかで確認するからですね。GoogleTestとかJUnitとかがTDDのフレームワークにあたります。
「TDDの単体テストを書く」=「フレームワークを選んで、テストコードを書く」だそうです。
テストコードは
1.コンパイルは通らなくていいので、動作しないテストを書く(赤)
2.テストを通すコードを書く(青)
3.リファクター(きれいに成型する)
という順で書いていくそうです。
フレームワークによるかもですが、赤がロジック的に正しくない状態、青が正しい状態を表します。この順で開発を進めていき、最終的にすべての機能・メソッドが青で通っている状態が完成形ということです。改修が入った場合も、問題のあるロジックになってしまった場合は赤になるので、すぐわかる、ということですね。
ブラックボックステスト
ホワイトボックステストのようなロジックではなく、ロジックが見えない状態(=ブラックボックス)となった、仕様確認寄りのテストになります。
ブラックボックステストでは
入力・出力・計算(処理)・データの保存
を意識します。よく、ケースを起こす際にテスト観点の洗い出しをすることがありますが、それもこの4つを意識しているんだと自覚すると、やりやすくなるかもしれません。
以下に列挙する手法は、その4つを適切にテストするための手法になります。
同値分割
これで確認するのは、有効な値と無効な値の組み合わせで、ちゃんと動作するかということ。
まずは有効同値(プログラムが期待する値)、無効同値(有効同値以外の値)にわけます。(「有効同値クラス、無効同値クラスという部分集合である」という考え方ですので、以下から「クラス」、「集合」という言葉を利用することがあります。)
ここでは入力AとBがあり、有効同値を1~999、無効同値をそれ以外とします。
A有効×B有効、A無効(正の値)×B無効(正の値)、A無効(正の値)×B無効(負の値)、A無効(負の値)×B無効(負の値)、A無効(0)×B無効(正の値)…
という感じで有効・無効の組み合わせを全通りやると「強い同値クラステスト」になります。有効×有効はもちろん理論的には999×999通りです。
これが基本の考え方です。
しかし、これではケース数がすごいことになってしまいます。そこで「実践的な同値クラステスト」を考える必要があります。
実践的同値クラステストは、同値クラス1つに対してテストケースを1つ割り当てるという考え方です。
参考書ではもっとわかりやすいグラフがあったのですが、用意できていないので、ひとまず表で見ていきます。
有効同値クラス | 無効同値クラス(正) | 無効同値クラス(負) | 無効同値クラス(0) | |
入力A | 1~999 | 1000以上 | 0未満 | 0 |
入力B | 1~999 | 1000以上 | 0未満 | 0 |
境界値分析
これで確認するのは、範囲の境界の近くでちゃんと分岐ができているかということ。
同値分析と一緒に使われる手法になります。境界値分析では、下記のエラーを検出することを意識します。
ありがちなエラー1:閉包関係バグ(>と>=の差)
ありがちなエラー2:設定値間違い(10までなのに間違えて20って書いたとか)
ありがちなエラー3:境界がない(elseの書き忘れとか)
ありがちなエラー4:不必要な境界(10以上は全部処理してOKなのに10までしか処理できないとか)
この4タイプを念頭において、入力の有効同値クラス、無効同値クラス、境界値を表にすると良いそうです。
有効同値クラス | 無効同値クラス | 境界値 | |
入力A | 1~999 | 0以下,1000以上 | 0,1,999,1000 |
入力B | 1~999 | 0以下,1000以上 | 0,1,999,1000 |
境界のテストをするためのポイントは、異なる処理が行われる一番近い2点のケースをすることです。表の「境界値」がテストすべきケースにあたります。
このくらいシンプルだと表も簡単ですが、業務ではもっと複雑な入力もあり、表を書いている場合ではない、といったこともあります。
そんな時の対策として、以下のようなデータの組み合わせで考えると良いそうです。
★良いデータ
・ユーザーがよく使いそうな値
・プログラム許容最小値
・プログラム許容最大値
・0
★悪いデータ
・異様に小さい値
・異様に大きい値
・長い値
・無効な値
ディシジョンテーブル
入力の組み合わせを表にして、その入力に対する動作・出力を明記する表。
多数の入力に多数の出力がある場合に有効です。表にできるレベルなので、小さめのプログラムとか、大きいシステムの小さい機能とかを確認するようなものになります。
あくまで表なので、パターンを整理するための手法だと理解しています。
画面の活性制御程度の簡単な例で表を作ってみます。
項目 | パターン1 | パターン2 | パターン3 | |
操作 | チェックボックス1 | ○ | - | ○ |
チェックボックス2 | - | ○ | ○ | |
想定結果 | テキストボックス1 | 活性 | 非活性 | 活性 |
テキストボックス2 | 非活性 | 活性 | 活性 |
「○」はチェックあり、「-」はチェックなしです。テキストボックス1の活性状態はチェックボックス1に連動します。テキストボックス2はチェックボックス2と連動します。
この程度の簡単なものでも、テスト時に文章で読んで理解するよりわかりやすいのではないでしょうか。
操作や想定結果は項目名でなくとも良いです。よく使われている例はクーポンによる値引きを適用する条件と、その条件下で適用される割引率のパターンです。
参考書と合わせて以下のページを参照しました。
GUIに対するテスト
1.状態遷移テスト
状態A→状態Bに変化した時の動作を確認するテスト。
状態をモデル化して、各遷移時のテストをしていきます。GUIソフトウェア、オブジェクト指向ソフトウェア、通信プロトコルなどのテストに向いているとのことです。
実のところ、業務であまり使ったことがないため、うまく例を考えられなかったので、ここは他の方のページに譲ります。いずれ自分で例が書けるようになったら書きます。
2.モンキーテスト
アドリブテスト、アッドホックテスト、ランダムテストも同義です。適当に入力したり操作してバグを出す手法のことです。
特にテストケースを作るようなタイプのものではありません。これで見つかるバグもそこそこあるものの、これだけでほとんどが見つかる、ということでもないとのことです。前述の様々なテスト手法を駆使したあとに、最後のバグだしの気持ちでやると良いかもしれません。
ブラックボックス全体を通して
入力がある→境界値テスト
入力が複数ある→ディシジョンテーブル
遷移がある→状態遷移テスト
の順で考えてケースを起こしていくのが良いようです。
業務で役立った手法
画面の制御はケースは、ディシジョンテーブルが案外使える
今回のテスト手法を学ぶことにしたきっかけは、テストケースの表現方法に困っていたことでした。
画面制御で複数項目をチェックしたりしなかったり、パターンもどこまで出せばいいかわからない。文言だけでは煩雑だし、網羅できているかどうかもよくわからない。レビューでマトリクスにした方がいいと言われてもどう書くべきかわからない。
この解決方法の1つとして大いに役立ったのが、ディシジョンテーブルでした。
パターンが見やすく書けるようになり、今回学んだ手法の中で一番役に立ちました。
おわりに
なんとなくでやってきたテストについて、改めて学んでみて、手法以外にも改めて意識するようになった(意識するようにしたい)ことがあったので、書き留めておきます。
誰が見てもできる確認手順、文言になっているか
これはテスト手法というより、私自身が普段のレビューで再三言われていることでもありますが、努めて意識するようになりました。
前述のディシジョンテーブルしかり、ドキュメントは見やすく、わかりやすくが重要です。並行して設計についても学習を進めた中で、ドキュメント化は他者に情報共有することが目的だと書かれていました。当たり前のようで忘れがちなところです。テストケースを作成するには、適切な手法を用いていることと同時に、誰にでも理解できるドキュメントを作成することも必要です。
テストケース以外で自分でもバグを出す方法を考えておく
私の現場では、テスト専門の方々にチェックして頂くITの工程があります。結構な頻度で評価項目が足りなかったな、とか、意識してなかったな、というようなバグを見つけて頂いています。
開発者だとロジックがわかっているので、このテストケースができていれば問題ないでしょ、という気分に多々なります。しかし、それは気分だけで、参考書の筆者曰く、経験的にブラックボックステストまでで60%も出れば御の字、だそうです。本職のテスター並みにバグだししてやろうとは思いませんが、40%も残っていると、開発者でもできることはありそうです。モンキーテストくらい、時間があったらやってみよう、と考えるようになりました。
テストも奥が深いなぁ(小並感)。