こちらの講演時にいただいた質問への回答です。

「テスト自動化とテスト駆動開発」講演資料 - やっとむでぽん

質問10. 仕様変更時、テストが壊れるケースがあると思います。どのようにプログラム修正するのが良いでしょうか?リファクタリングとは異なり、新規開発のように先にテストを書くのでしょうか?

スライド88で、機能追加のプロセスを説明しました。仕様変更も機能追加と、手順は変わりません。

f:id:yach:20210409141139p:plain

仕様変更は「動作を変える」ことなので、リファクタリングとは違うものです。リファクタリングは動作を変えないままで、内部構造を変更します。余談ですが、リファクタリングを単なる「ソフトウェアの変更」という意味で使う場面を見るのですが、本来の意味とは違います。

この図の「自動テストがある場合」では、実装を終えてから、変更後の仕様に合わせてテストをまとめて変更します。すべてのテストが成功したら、仕様変更は完了です(もちろん見落としがなければです)。 同様に「テスト駆動開発の場合」では、まず変更後の仕様に合わせてテストを直します。ただし、TDDのサイクルは小さいので(数分~数十分)、すべての仕様変更を一気に盛り込めません。どうするかというと、仕様変更をブレークダウンして、テストと実装がすぐ終えられるように小さくしていきます。そのうえで、ひとつずつをテスト駆動開発で進めていきます。

たとえば、ユーザーがデータを操作する画面(ブラウザ)があって、そこに項目を1個増やすような仕様変更を考えてみます。ブレークダウンした結果は、たとえば次のようになります(あくまで一例です)。

  • 入力画面に項目を増やす
  • テーブルに入力項目に対応するカラムを増やす
  • 保存処理で新しい項目を保存する
  • 表示画面に項目を増やす
  • 入力画面のバリデーションを直す
  • 入力内容確認画面に項目を増やす

このようにすると、ソフトウェアは「動作するきれいなコード」を保ちながら、機能としては中途半端な状態を経由して、少しずつ完成に近づいていきます。入力はできるけど保存されない、保存されてるけど表示できない…それぞれのステップでテストを書き足して(書き換えて)、実装し、リファクタリングまでします。

このように進めるのは、ふわっとした言い方ですが「安全」のため、ないしは不安が小さいままで進めるためです。一気にテストを直すと、大量にテストが失敗する状態になります。すべてテストが成功するのは、入力し、保存し、表示ができて、バリデーションまで…それまでの間、テストが成功しているから壊れていない、という状態がおあずけになります。壊れていない保証がないまま直すのは、不安です。途中で思うようにいかなくなって、あれこれ試行錯誤すれば、不安はさらに増大していきます。

テストが成功している状態は、「動作するきれいなコード」なので、安全エリアです。不安になったり、先に進むやり方がわからなくなったら、いったん安全エリアまで戻り、何も壊していない状態を確認してから、あらためて次の一歩の作戦を考えます。この安全エリアにいつでもすぐ戻れるように、仕様を少しずつテストにして、実装していくのです。

不安の程度、安全エリアからの距離というのは、多分に心理的なものです。とりわけ、担当者の理解度、自信、体調、疲労度、言語の練度などによって大きく変わります。スキルのある人が元気で頭がフル回転している月曜日であれば、もしかしたら一気にすべて書き上げてしまえるかもしれません。自信を持って「大きな一歩」で飛び越えられるなら、それに越したことはありません。しかしそこまでスキルのない人、このコードを初めて見るメンバー、疲れ果ててる金曜日、今夜の試合が楽しみで気もそぞろ……というような場合は、もっと小さな一歩ずつにブレークダウンしたほうが安全になります。

和田卓人さんは、こうしたスキルや状況に応じて一歩の大きさを変えることを「間合いを調整する」と表現しています。自信があるなら間合いを広くとって、一発で決める。自信がなければ間合いを狭くして、細かく密接に追随する。テスト駆動開発のメリットとは、そうした間合いを自由自在に調整して最適な仕事の進め方を選べるように、開発者がなる、そういう能力を獲得する、と言うこともできます。

質問11. カバレッジを上げようとすると費用対効果が悪くなっていくという理解です。どこまでテストすべきか指針となるものはありますか? 例.業務ロジックのみテストコードを書く…など

前の質問への回答と関連しますが、和田卓人さんはテスト駆動開発においては「不安を手がかりにしてテストを書きます」と言っています。プログラマーは自分の心の声を聞いて、自分が対象のコードをどのくらい自信を持って理解しているか把握します。その心の声が「ちょっとヤバイかも」と囁くなら、テストを書いて確認する。というのが、とても曖昧に聞こえますが、現場の方へのアドバイスになります。

自明ではありますが、大事なところは丁寧にテストします。お金を扱う、SoRで保存すべきデータを保存する、個人に紐付く情報(個人情報に限らず)、複雑なビジネスロジック、などは仕様の検証、設計のレビューなども含めて手厚くテストをします。テストを自動化する場合でも、どういうテスト設計をしたのか、それぞれのテストケースにどんな根拠があるのか、あとからわかるようにしておいたほうがいいでしょう。

自分たちのコードでない部分も、場合によってはテストを厚くします。外部からデータを取り込む場合(外部にどんなバグがあるかわからない)、ライブラリを使う場合(ライブラリを作った人をどれだけ信用するか、ライブラリのバージョンアップで急に挙動が変わったら気づけるか)。

こうしたアプローチは、リスクベースドテストの一種とも言えます。危険そうなのはどこか、問題があったとき損害が大きくなるのはどこか、自分たちが把握しきっていないのはどこか、そうした観点を用います。リスクベースドテストについて、秋山浩一さんのブログを引用して紹介しておきます。(秋山さんは、スライド内で紹介した『ソフトウェアテスト技法ドリル』の著者。)

リスクベースドテストとは

『ISTQBテスト技術者資格制度 Foundation Level シラバス 日本語版 Version 2018.J03』では、「リスクベースドテスト」をテスト戦略のひとつ(分析的テスト戦略)に位置付けています。該当部分を引用します。

テスト戦略は、通常、プロダクトまたは組織のレベルでの、テストプロセスに関する汎用的な考え方を提供する。テスト戦略の一般的な種類は以下の通りである。

・ 分析的:いくつかの要因(要件やリスクなど)の分析に基づく。分析的アプローチの例としては、リスクのレベルに基づいてテストを設計し優先度付けするリスクベースドテストがある。

さらに「5.5.3 リスクベースドテストとプロダクト品質」の項で詳細な説明があります。こちらは、ちょっと長いので要約しています。(オリジナルが気になる方は、JSTQB FLシラバスの71-72ページをご一読ください。)

リスクを使用して「テストの内容・優先順位・実施順番を決めること」ができる。また、テストを「プロダクトリスクを減らし残存リスクを明らかにすることを目的とした活動」と位置付けることができる。
識別されたリスクに対して、(期待しない事象が)「発生する可能性」と「発生した場合の影響」を評価する。

(残存リスクに対して)リスクマネジメントで実施することは下記の通り。
・ リスクを分析する(定期的に再評価する)。
・ コンティンジェンシープラン(緊急対応計画)を作る。

note.com

コードカバレッジの数値は、手を入れるべき場所を見つけるために使うとよいです。達成目標にすると、機能不全を起こします。全体が80%だからOKとかNGとかいうのではなく、特にカバレッジの低いところがないか、ドリルダウンしていってテストが足りていないところを見つけるという使い方がよいです。

仕様に対するカバレッジも、テストの不足を見つけるのに使えます。コードカバレッジのように自動で測定するのは難しいかもしれませんが、仕様ドキュメントに対するテストのカバレッジや、仕様としてのビジネスロジックの場合分けに対するカバレッジ、画面上の操作に対するカバレッジなどを調べます。

そういう意味で、どういうテストを書くべきかという基準はあったほうがよいでしょう。詳細な定義やルールではなく、プログラマーを導けるようなものにするとよいと思います。これは思いつきですが、

  • ユーザーのやりそうな操作はすべてカバーする
  • 外部データ入力は創意工夫して変なパターンをテストする(もちろん普通のケースはちゃんとやる)
  • 異常なパラメータだったら異常な結果になるかテストする
  • 境界値、同値クラスを意識した上で、変なパラメータを選ぶ

のようなものです。○×を付けるようなものではなく、「どうしてこのテストがいる/いらないと思ったのか?」と議論できるようなものだとよいのではないでしょうか。(すみません、この部分は本当に思いつきなので、あまり信用されないでください。面白そうだったら試してみてください。)

10年前くらいのJavaの経験だと、コードカバレッジ(C0)は80~90%くらいが良い感じで、それより低かったら、なにか抜けていそうなので調べるというふうにしていました。95%以上にするには、テストが大変になったり、無駄に設計を変えないといけなかったり、現実には絶対起きない例外をおこしたりしないといけないので、やらないようにしていました。

コードカバレッジでは検出できない抜け漏れもあります。数字だけ意識していると、こうした部分のテストを見落とします。