まえがき
世間ではテストをしないこと/テストを書かないことを悪とするみたいな文化が定着してきたのか、テストを書かない開発というのが減ってきてると思います。
では「正しくテストを書けているか」「テストを書く文化を生かしているか」というとどうでしょう?
これらの問いに答えられずテストを書くことをゴールにして満足していると、それは宝の持ち腐れならぬテストコードの持ち腐れとなります。
執筆のモチベーション
この記事を印刷して札束のごとくビンタするのが目的です。
以下に該当すること”だけ”を考えているような場合はビンタされるかもしれません。(絶対ではないですが危ない可能性があります)
- うちはテスト書いてるから大丈夫!ちゃんと開発工数とは別に単体テスト工数も見積もってるよ!
- ちゃんとホワイトボックス的に書いてカバレッジ担保しているからうちはちゃんとテストしてるよ!
- コード修正の都度ちゃんとテストコードもメンテしてるよ!
- 自動テストのためにもテストは欠かさず書いているよ!
とはいえ、筆者は平和主義者なのでビンタ被害者を一人でも減らすべく、この記事を通してテストコードの価値を理解せずに脳死で「テストコードを書くことを目的化」してしまっているビンタ対象予備軍に
「価値あるテストを書くこと」「テストを書く文化そのものの価値を最大化させること」を今一度考え直してもらうことをゴールとします。
それでもわからん人には仕方なくビンタです。
テストコードに対するありがちな誤解
「今は急いでいるからテストコードは後で書こう」「テストコードのカバレッジがxx%であることを単体テスト完了基準とする」…といったような意識をしている方は、
そもそもテストコードを書く意味やその責務を誤解している可能性があります。
まずはこの誤解を解き、テストコードの価値を最大化するスタート地点に立ってもらいます。
テストコードの責務
まず、テストを書くことを目的化/ゴール化している方々に理解してほしいのは、テストを書くということは目的ではなく手段です。
テストとは、そのソフトウェアが正しく動くことを担保するための大事な作業です。
テストコードはその正しく動くことを保証する責務をプログラムに委譲しているに過ぎません。
故に、テストを書くということは「ソフトウェアを正しい状態に保つ」という目的に対する手段であるということです。
正しく動くの定義
ではテストコードで保証すべき「正しさ」とは何でしょう?
境界値検査やループ処理等の処理を網羅させカバレッジが100%であることでしょうか?
確かにカバレッジはそのソフトウェアの品質を計測する上で重要な指標の一つですが、これもあくまで品質を測る手段でしかありません。
正しく動くソフトウェアとは、そのソフトウェアで解決したい課題を解決しているソフトウェアです。
ソフトウェア開発のモチベーションには必ず、そのソフトウェアで解決したいかだいがあり、その解決方法を仕様として定義しそれに沿って開発を行います。
故に、正しく動く = 仕様通り動くといっていいでしょう。
テストコードによるテストが通る = 仕様どおり(正しく)実装されていること
を前提とし、その上でカバレッジ等の品質を測らなければ意味がありません。
テストを書くことを正しく目的化する
前章ではテストを書くということに関する意識を見直してもらうべく、テストコードの責務やその目的に関して書きました。
ただ、これらはあくまで誤った認識のままでの目的化であることを指摘しているだけであって、目的化そのものを否定している訳ではありません。
本章では、仕様を担保するテストコードをどう書いていくか?そうすると何がいいのか?といったような正しい目的化を行うための話をしようと思います。
「こうできている」を担保することから「こう動く」も担保することへ
テストコードのアンチパターンとして陥りやすいもののほとんどが、「テスト対象がどう実装されているか"のみ"を担保しているテストコード」です。
具体的には、ただカバレッジを満たすためのホワイトボックステストのみを行っている状態です。
ホワイトボックステストは、これらのテストはテスト対象の内部構造を理解した上で、いかなる条件やループ回数、実行引数であってもソフトウェアが壊れないことを検査できていることを観点としています。
カバレッジはこの検査の網羅率を示している指標に過ぎないため、「正しく作られている」指標ではありません。
なんども書いているようにカバレッジを満たすことは大事なことですが、これをテストコードのゴールとするのは不十分です。
では、「正しく作られている」を観点としたテストコードをどう書くのかというと、ビジネスとして想定しうるINPUTに対して期待する振る舞いやOUTPUTを出せるか? を観点としたテストケースを定義し、それを満たすブラックボックス的な観点のテストコードを書くことです。
例えば、"有効な顧客が商品を購入した場合に決済を行い、購入履歴を作成する"のように、プログラムの振る舞いをケースとするのではなく、ビジネス目線でのソフトウェアの振る舞いをテストケースとするといった感じです。
テストコードはブラックボックス/ホワイトボックス双方の観点を満たして初めて価値のあるものとなります。
「どこまでできているか」から「どこまでの動きを保証しているか」
テストコードが「ソフトウェアが正しく動く」ことを担保できるように作られているならば、「ソフトウェアがどこまでできているか?」の指標も自ずと変わってきます。
よく現場で遭遇する進捗報告を例に、↑が何を言っているのかを説明します。
自身が担当している実装に関して「どこまで完了しているか?」という問いに対して、実装ができている箇所の総量を完了の基準として報告していることはないでしょうか?
例として、「xxクラスorメソッドの実装を終えました」や「xx行ほど実装完了してxx行残ってます」のような報告です。
確かに「どこまで実装したか?」に答えるならばそれでいいのかもしれないですが、マネージャやプロダクトオーナーといったステークホルダが聞きたいのは、「どこまで正しく動く状態なのか?」ではないでしょうか?
ビジネスの観点でいうと、1000行で10機能ほどに見積もられた実装のうち700行書いてるか200行書いてるかなんてぶっちゃけどうでもよくて、本当に知りたいのは、「いくつの機能が既に価値を出せる状態なのか?」です。
それを計測するのにも、上述の「正しく動く」を担保するテストコードが真価を発揮します。
「xx機能はテストをパスしています」というコミニュケーションを行ったほうが、そのソフトウェア開発に進捗を正しく測ることができるし、ビジネス側も「どこまでのユースケースや課題を解決できている状態なのか」を測りやすいです。
こういったコミニュケーションのツールとしてもテストコードは有用であり、どう動いているべきか、それがどこまで保証できているかをテストコードを通して伝える事ができます。
手段としてのテスト駆動開発
ここまでテストコードをどう書くか?どう活かすか?的なことを書いてきましたが、これらを達成する手段としてテスト駆動開発(TDD) という手法があります。
知らない人向けに超簡単に説明すると、テストコードを先に書き、そのテストコードがPassするようにプログラムを書いていき、テストコードがすべてPassするまで対象のプログラムに処理をインクリメントしていく開発をテスト駆動開発と呼びます。
テストコードの話をすれば2個1レベルで出てくるこのワードですが、これもテストコードを書くこと同様に「テスト駆動開発を行うこと」を目的にしているような話を度々聞きます。
ですが、テスト駆動開発はここまで書いてきた「テストコードの価値を最大化する」ことを目的とした手段でしかありません。
これは筆者の持論ですが、テスト駆動開発の本質はテストコードをソフトウェアの振る舞い(仕様)を定義するコミニュケーションツールとすることで、テストコード=仕様を満たすようにソフトウェアをインクリメントしていく
ことだと考えてます。
正しい文化がもたらす嬉しい副作用
ここまで読んで頂いたのならばテストコードが「正しく動くソフトウェア」や、「どこまで正しくできているか」を担保するのに効果を発揮することがきっと伝わっていると思いますが、テストコードがもたらす価値はこれだけじゃありません。
変更への耐性と対応スピード
テストコードが正しく上記の機能を満たしているならば、当該ソフトウェアに対する変更ストレスは格段に下がり、自ずと変更に強いソフトウェアや開発文化を形成できます。
具体的にはリファクタリング時の心理障壁を低くすることややリグレッションの高速化、CI/CDによるそれらの自動化です。
ソフトウェアの変更は継続的に価値を届けていくためには避けては通れません。
変更に対する影響の調査や再テスト等のように、ソフトウェアの変更のストレスとなるものは少なからず存在します。
それらのストレスを軽減するためにも、変更後の動作保証やそのテストそのものの自動化を行うといった手段を取ることが多いと思いますが、そのためには「正しく動く」を保証するテストコードが必要不可欠です。
ですが逆言うとテストコードを正しく活用できているならば、上記のモダンな開発プロセスを最大限に活用することができ、結果として変更に強いソフトウェアを作ることができます。
自然といいコードを書く意識やスキルが身についてくる
良いコードは一般的には、”疎結合かつ高凝集”なコードとされています。
これを満たすためには、プログラムの各コンポーネントの責務を明確にする必要があります。
テストコードを予め書いていれば、保証すべき振る舞いをビジネス単位に整理することもでき、結果として単体でビジネスを満たす責務を持ったモジュールの設計もしやすくなります。
レビュー時の観点の絞り込み
そもそもとして、ちゃんと動いているか?担保すべきものが担任しきれているか?はテストコードで担保されているため、
開発時のレビューでは、プログラムそのものの再利用性とか可読性とかそういうのに観点を絞ったり、エッジケースの検出に観点しぼることができます。
※逆に言えば、テストコードのレビューではビジネスとして満たすべきものを担保できているか?をしっかり観点としなければなりません。
まとめ
ここまでの話をまとめると以下のような感じになります。
- テストコードはブラックボックス/ホワイトボックス双方の観点を持って初めて意味を成す。
- テストコードを正しく書けばビジネスと開発をつなぐコミニュケーションツールとなる。
- テスト駆動開発は↑を満たすための開発手法(by 筆者)
- テストコードを正しく書くことで、変更に強いソフトウェアや開発プロセスを作ることができる。
おわりに
今回ちょっと強めのタイトルをつけましたが、テストコードを書くことやテスト駆動開発をしていることだけを正義とするような風潮をたまにみかけていたため筆を執りました。
すべては良いソフトウェアを作るため/良いソフトウェアであり続けるための手段であることをただ書きたかったという感じです。
また、この良いソフトウェアの定義として、良いソフトウェア=変更に強いソフトウェアのような文脈で本記事を書きましたが、
そもそもなぜそういう前提の上で記事を書いたのか?という点に関しては、筆者が過去に作った以下の資料を併せて読んでいただけるといいかなーと思います。(というただの宣伝)
ここまで読んでくださりありがとうございました。
最後にLGTMいただけると筆者の励みになりますので、心に余裕がある方はよろしくおねがいします!