TEF道(Test Engineer's Forum 北海道)のメンバーと、「テストケースの実行効率と説明責任」というテーマでディスカッションしていた頃からつらつらと考えていたアイデアのメモです。朝一の殴り書きなので、雑はご容赦。議論のきっかけになると嬉しい。
なおその時の議論の一部をInSTA(International Workshop on Software Test Architecture)の発表資料にし(てくれ)たものがコチラ。
なお、Twitterでの以下の会話もキッカケになっています。
テストで確認したい観点を、少数のテストケースに詰め込まず、できるだけ小さいテストケースに別々に割り当てることを、「テストケースの責務の分割」と言ったりしますか?
— Kazu SUZUKI (@kz_suzuki) 2020年2月12日
モノリシック/アトミックなテストケース
美しいテストケースはどれも似たものですが、汚いテストケースはいずれもそれぞれに汚いものです。
とはいえ、テストケースを汚くする要素の一つに、「何でも詰め込み過ぎ」というものがあるということには異論ないでしょう*1。一つのテストケースでいっぺんにいろいろ確認しようとするあまり、長大で複雑な、全体としてよくわからない内容になってしまうことがあります。
一方で、一つのテストケースが一つのことだけを確認するようにデザインされたテストケース群もあります。こちらはテストケースとしての見通しがよく、後述するように保守性も相対的に高くなりそうです。
ソフトウェア界隈の言葉を借用するなら、前者は monolithic(モノリシック)であり、後者は atomic(アトミック)と表現してもいいかもしれません。
その明確な定義はちょっとおいておくとして、まずモノリシックなテストケースの特徴について整理してみます。
モノリシックなテストケースはなぜ生まれる?
そもそも、なぜテストケースはモノリシックになっていくのでしょう。
一つは、「効率よく実行したい」というモチベーションです。
よく分割された*2テストケースは、それぞれが短い一方で、テストケース間のステップの重複も発生しがち。なぜなら、テストケース間の依存関係を低くするために、各テストケースを独立して開始/終了できるようにするためです。たとえばWebアプリケーションを考えてみると、ほぼすべてのテストケースで「ログイン」「ログアウト」が発生するといった具合です。
その他にも、テストケースの長時間化の原因となるステップ、たとえば「システム障害」が複数のケースに入っていたりすると、「いっぺんに確認したい」誘惑が強くものです。一度の「障害」で、いっぺんにいろんな挙動を確認した方がよさそうに思えますから。
手動テストにおいて、こういうテストケースの実行を効率化するには以下の二つの方法があります。
- テストケース自体はそのままにして、確認ポイントと関係ないステップを省略する。たとえば、ログアウトしないまま次の確認項目に進む
- テストケースを「統合」することで、一度のログイン/ログアウトでたくさんの確認項目を消化できるようにする
この2.を選んだ場合、アトミック性を下げることになります。またこの先、別のテスト観点が追加された場合にも同じ「統合」 が行われ、そのテストケースがさらに肥大化しがちです。
モノリシックなテストケースの特徴
モノリシックなテストケースの困った点について述べていきます。
テストケースの見通し
テストケースのアトミック性が高いと、何を目的としたテストなのかが読み取りやすいです。
もちろん適切な情報(たとえばテストコードの名前、ヘッダ、コメント)がテストケースに与えられていることが望ましいですが、テストステップ/テストスクリプトを読めばやりたいことがおおむねわかる。
モノリシックなテストケースでは、それが途端に難しくなります。
観点を詰め込むために不自然な流れになっていることもあり、「やたら長いけれど、ユースケーステストになっているのかといえばそうでもない」ようなテストケースが生まれがちです。
テストケースの保守性
見通しが悪いと、テストケースを直すことも難しくなります。各ステップの必要性が見えづらく、ステップ間の関連性も複雑になっているためです。
やたら迂遠な流れになっていても、その流れが必然のものなのか、建て増しによる弊害なのかが判断しづらい。迂闊に触ることができないので、「最適化」が妨げられます。「最適化」した結果、実は一部の観点の確認が失われていた、あるいは確認になっていなかったということが起こりえます。恐ろしいことに、それに気づけない可能性が高い。
同じ理由で、あるテスト観点が不要となった場合も、そのテスト観点を大きなテストケースから「剥離」することは難しくなります。効率化したつもりが、その逆の効果を生んでしまうわけです。
テストケースのトレーサビリティ
観点の集合体になってしまったモノリシックなテストケースでは、「もともと確認したかったこと」とのトレーサビリティが失われることがあります。
アトミック性の高いテストケース10個を一つの(モノリシックな)テストケースにパックしたうえで、その「大きなテストケース」のステップや確認項目が変化していくと、元の10個をトレースすることが難しくなっていきます。すると、「この10個の観点は、このテストケースで確認できているはず」という信念だけが残り、実際に確認できているかは実はあいまいということになります。
自動テスト失敗時の対処
まず単純に、モノリシックで長大なテストケースをそのまま自動化すると、テスト実行時間が相対的に長くなります。このテストケースが fail となった場合、それまでかかった時間が無駄になってしまいますし、複数の観点のうちどれが確認済みで、どれが未確認なのかをいちいち調べる必要が出てきます。
アトミック性が高いテストケースであれば、fail したときのダメージが比較的低いです。短くてシンプルなテストケースなので、やり直しも簡単です。
ただし、多くのテストケースに共通するような根本部分にバグがあった場合は話が変わってきます。自動実行を制御しておかないと、「ほぼすべての自動テストケースが fail する」という事態になってしまいます*3。
テスト管理の容易性
テスト管理ツールなどを使って、テストケースに属性情報を付加している場合を考えてみましょう。
たとえば「品質特性」とか「テスト観点」といったラベルを使っているとして、アトミック性の低いテストケースには、このラベルが大量に貼られることになります。
リグレッションテストにおいて最低限確認したいテストケースを、これらのラベルを使って絞り込もうにも、多くのテストケースに多くのラベルが貼られているため、有効な絞り込みができない、という弊害が考えられます。
まとまっていないけど、まとめ
目的、テスト観点を詰め込み過ぎた「モノリシックなテストケース」について簡単に考えてみました。
名前があると議論がしやすいのと、あとなんとなく monolithic test cases とかいうとかっこいいからです。
プログラム設計のアナロジーを使って「責務の分担」「凝集度」「結合度」といった概念も流用して、テストケース(群)の品質特性を考えることができるのかもしれません。こういうの楽しいですよね。