テストコードは必要か?(自動テスト・テスト駆動開発<TDD>について考える)
はじめに
本記事は、エンジニアが自動テストコードを記述することの有用性と、テスト駆動開発の導入目的を改めて考え、まとめた記事になります。
結論から言えば、「テストコードは重要なので、開発者が書いていこうね」っていう話です。
想定読者は以下のような方々です。テストコードの重要性について再認識する機会になれば幸いです。
未経験からエンジニアになり間もない方。
テストコードの重要性がいまいちわかっていない方。
納期までの時間がないといった何らかの理由からテストコードを書けていない方。
上司やチームメンバー、顧客にテストコードを書くことのメリットを説明する機会がある方。
なぜテストが必要なのか
後輩から「なぜ余分な工数を割いてまでテストコードを追加する必要があるのでしょうか?」と尋ねられたらあなたは何と答えるでしょう?
私は自分の経験やテストに関する書籍などから、テストコードを書く意義は大きく4つあると考えています。
- 不具合の早期検知・バグの減少
- 疎結合なシステムに開発が向かう
- リファクタリングの促進
- 新規参画者の仕様理解・コードリーディングの効率up
以下、それぞれについてお話ししていきます。
1.不具合の早期検知・バグの減少
これは一番最初に思いつきそうな効果で、言わずもがなだと思います。
自動テストを頻繁に実行することで不具合を本番リリース前に検知できます。また、開発中においてもコーナーケースを明示的にテストコードに落とすことで、バグの減少に繋がるはずです。
また、本番等で発生してしまったバグに対してテストコードを書いた上で修正することで、そのバグが再び発現することを防ぎます。
※一度起こったミスが今後何かの拍子に再び発現する可能性は十分にあります。自動テストを書き、毎回実行することで再発を防ぎましょう。
2.疎結合なシステムに開発が向かう
テストを設計することで、クラスやメソッドがテストしやすい形になり、自然と機能間の結合度を下げる効果が期待できます。
コードを単体テストするためには機能(クラスやメソッド)を役割ごとに分離し、DBなど外部システムから可能な限り切り離し、外から結果を確認できるようにする必要があります。その結果、クラスやメソッド同士に余計な依存関係が生まれず、結合度が下がります。結合度が下がるとシステムの保守性や拡張性も向上し、バグ修正や機能追加もしやすくなるというメリットがあります。
できるエンジニアはテストコードの設計も上手な印象です。「テストはソフトウェアエンジニアとしてのスキルの一つ」というのをどこかで聞いたが、確かに納得です。
※場合によっては外部システムをモックやスタブを使わずに実際に接続して試験したいケースもでてくると思います。そうしたときに、モックを使うのか、実際に接続するのかはテスト側から制御することになるはずです。そのときにクラスやメソッドが外部システムに密結合していたらテスト側から制御できません。そのためにも結合度を下げる必要があります。
3.リファクタリングの促進
勇気を持ってリファクタリングができるようになります。
前提として、リファクタリングは常に行なっていくべきです。気になるコードを放っておくと、それはやがて肥大化し、本番障害につながることもあります。将来手がつけられなくなる前に、早いうちに潰しておくのが一番です。
ただし、リファクタリングの際にテストがないと、修正箇所のクラスないしはメソッドの振る舞いが変わっていないことを保証できません。毎回毎回手動でテストしていてはリファクタリングに手を出しにくくなりますし、時間がもったいないです。
システムをより良いものをしていくために、リファクタリングは必要不可欠です。
そのためにもテストコードが必要です。
4.新規参画者の仕様理解・コードリーディングの効率up
プロジェクトに新規で参画した開発者は少なからずコードを理解しようと既存コードを読むはずです。
そこにテストコードがあるとコードの理解促進につながります。
例えば、あるメソッドはマイナス値を許容するのか、最大値・最小値は存在するのか、どのような結果が返却されるのか、テストを読むことで理解することができます。要は使用例をすぐ確認できるのです。
著書「達人プログラマー(第2版): 熟達に向けたあなたの旅」では、「テストはコードのユーザー第1号」であると述べており、まさにその通りだと思いました。
実際の使用例を見ることで、仕様やコードの役割への理解が深まると言えます。
その他副次効果
テストの効果で付け足しておきたいメリットは、開発者の精神安定剤になることです。
テストを書いたことで自分のコードに自信が持てる、自動テストにより意図していない機能に悪影響がでていないことを確認できる、などテストを書けば夜も安心して眠れます。
テスト駆動開発とは
テスト駆動開発はTDD(Test-Driven Development)と呼ばれ、今となっては有名は開発手法なので、ご存知の方は多いと思います。
TDDの基本サイクルは以下の通りで、この1から4のサイクルを繰り返していき、開発を進めます。
- 実現した機能(クラスやメソッド)を実装した場合に、それをパスするテストを最初に記述する。
- テストを実行し、テストが失敗することを確認する。(RED)
- テストがパスするコードを記述し、テストが成功することを確認する。(GREEN)
- リファクタリングを行う。この際もテストが成功することを確認する。
テスト駆動開発の導入目的
テスト駆動開発のポイントは、実機能のコーディングの前にテストを記述することで、必ずテストコードが存在するシステムが出来上がることです。
はじめにテストコードを考えることで、その機能の役割を明確化し、クラスやメソッドはその機能を実現するだけのものとなり、機能間の余計な依存関係が生まれにくいです。
「なぜテストが必要なのか」で述べたメリットを享受することができる開発手法となります。
テスト駆動開発のデメリット
システム開発に銀の弾丸が存在しないと言われるように、そんなTDDにもデメリットは存在します。以下は考えられるデメリットになります。
- テストコードを書くことが全ての目的となり、テストカバレージばかり気にしてしまって本質ではないところに時間をかけてしまうこと
- 意味のないテストを大量に用意してしまうおそれがあること
- 慣れるまで開発工数が多くかかってしまうこと
- チーム全体で取り組まないとあまり意味がないこと(チーム全体への理解と普及が必要)
開発手法にトレードオフはつきものです。私は必ずしもTDDを導入する必要はないと考えています。
納期が厳しく、かつTDD経験者が一人もいないようなチームなら、TDDを導入する余裕はないかもしれません。ただし、テストコードを書かなくていい理由にはならないとも思います。
TDDではなくとも、開発しながらテストコードを書くことは可能です。
TDDに関わらず、開発者ならテストコードは責任を持って書くようにすると良いと考えます。
私の失敗談
私はとあるアプリケーション開発で、本番リリースまでの納期が非常に厳しく、結果的にテストコードが少なくなってしまったシステム開発に参画していた経験があります。健康的な生活とテストコードを代償に無事本番リリースには間に合いました。
(当時はテストコードに思いを寄せる余裕もなかったのが実際のところ。)
ただ、テストコードがないプロジェクトはその後どのような運命を辿るのでしょうか?
行き着く先は、保守がしにくく、バグ修正や新規機能追加などにスピード感を出せないシステムです。
リリース後に徐々にテストコードを充実させていこうという方針は立てたものの、機能追加などの要件で時間がなかなか取れないのが現実でした。
その結果、修正や新規機能追加を行うたびに、影響確認や無影響テストの実施やらで機能の本質ではない部分で時間を取られ、他への影響が本当にないのかという不安に駆られてしまうのです。。。
(テストコードをもっと書いておけば、と何度後悔したかわかりません。)
このプロジェクトの経験は、テストコードの必要性について改めて考えさせられる一件であり、とても良い経験ができたと思ってます。
TDDの導入はマストではないにしろ、自動テストコードを書くことは今日のシステム開発では必要不可欠だと再認識しました。現実問題、「後でテストコードを書いていこう」というその「後で」の時間はほとんどのケースで存在しないことが多いです。だからこそ、最初からできる限りテストコードを書くべきだと感じました。
もしあなたのチームで今テストコードを書いていないのであれば、明日からでも書き始めることをおすすめします。そのテストを書くコストは、近い将来必ず回収されていくはずです。
プロとしてユーザーのニーズを満たしつつ、保守や拡張のしやすいシステムを作りましょう!
そのためにテストコードは必要なのです!
さいごに
私もまだまだテストコードをうまく書けている自信はありませんが、日々実践しています。
頭では重要性は分かってはいるものの、すぐに実際に上手なテストコードを書くのは難しいことだと思います。
初めは「テストを書く」という工程を意識的に踏むことで慣れていき、徐々に上達していければ良いと考えています。
皆様のより良いシステム開発に本記事が少しでもお役に立てれば嬉しいです。
ここまで読んでいただきありがとうございました!
参考書籍
Discussion