目次
新しいソフトウェア開発の方法として注目を集めているのが、テスト駆動開発(Test-Driven Development:TDD)です。無駄なく開発ができることで注目をされていますが、従来の開発手法とはやや異なった流れとなるため、完全に理解しきれていない人がいるのも事実です。
本記事では、テスト駆動開発の概要と基本サイクル、メリット・デメリット、やり方について解説します。
株式会社SHIFTでは、重要なソフトウェアテストに関して豊富な実績とテストナレッジを保有しており、あらゆるお客様のニーズを満たしたテスト・品質保証を上流~下流(テスト計画・テスト設計・テスト実行・テスト品質管理)まで一気通貫でご依頼いただけます。
>>ソフトウェアテスト・第三者検証のページへ
>>導入事例ページへ
>>料金についてページへ
>>お問い合わせページへ
テスト駆動開発(TDD)とは?
テスト駆動開発とは、テストファーストな開発手法の1つです。従来行われてきた開発プロセス≒“ウォーターフォール型”に代表される、「設計→実装→テスト」のような直線を描くスタイルに対し、テスト駆動開発は「テスト→実装→リファクタリング」を何回も繰り返してプロダクトを成長させていくような開発手法を指します。
略称としてTDDと呼ばれることもあります。名称としてテスターが行うテストのように見えてしまうため、「ふるまい駆動開発(Behavior Driven Development)」と呼ばれることもあるようです。
テスト駆動開発(TDD)の基本サイクル
テスト駆動開発の基本サイクルは、主に、「レッド・グリーン・リファクタリング」から構成されています。
1.レッド:動作しない、おそらく最初のうちはコンパイルも通らないテストを1つ書く。
2.グリーン:そのテストを迅速に動作させる。このステップでは罪を犯してもよい。
3.リファクタリング:テストを通すために発生した重複をすべて除去する。
(引用元)『テスト駆動開発入門』 | Kent Beck 著 | 和田 卓人 訳
専門書によると上記のような記載がありますが、わかりやすく説明すると以下になります。
1.レッド:仕様に対して失敗する(エラーになる)テストコードを書く
2.グリーン:1をもとに成功する(パスする)コードを書く
3.リファクタリング:できたものに対して余分なものをそぎ落とし、きれいに整える
テスト駆動開発の目的
そもそも、なぜこのようなテスト駆動開発を行うのでしょうか。専門書では、以下のように記載されています。
「動作するきれいなコード」、ロン・ジェフリーズのこの簡潔な言葉は、TDD(テスト駆動開発)の目標である。動作するきれいなコードは、あらゆる理由で価値がある。
(引用元)『テスト駆動開発入門』 | Kent Beck 著 | 和田 卓人 訳
つまり、きれいな動くソースコードを書くことを目的としています。このきれいなコードを書いていくには、一般的に2つの方法があります。
・きれいなコードを書きながら動作するように直していく
・動作するコードを書きながらきれいに直していく
テスト駆動開発は後者です。「動作」していなければ製品としての価値はないというところに1つの理由があります。また、フィードバックが早ければ早いほど修正は容易になります。そのため、まずは動くように書き、フィードバックを得て素早く修正していきます。
アジャイル開発におけるテスト駆動開発
テスト駆動開発はアジャイル開発の実現に適した手法とされています。アジャイル開発とは、「計画→設計→実装→テスト」の小さなサイクルを連続させ、1つの成果物を完成させる手法です。アジャイル開発では、各サイクルのなかでテストを早期に行う必要があります。そのため、テスト駆動開発はアジャイル開発の実現に適した手法として、多くのアジャイル開発で取り入れられています。
アジャイル開発に関してはこちらもあわせてご覧ください。
>>アジャイル開発とは?意味や特徴、メリット・デメリットをわかりやすく解説のページへ
テスト駆動開発の種類
テスト駆動開発には4種類のテストがあります。
・ユニットテスト
・統合テスト
・回帰テスト
・受入テスト
基本的には、ユニットテストと統合テストの両方が連携してプログラムを標準的に保つ必要があり、テストの大部分を占めることになります。
ユニットテストにおいて、優れた設計者は設計したコードが失敗したかだけではなく、どのように失敗したのか、なぜ重要なのかを考えます。
統合テストでは、ソフトウェアを適切に保持するルートや方法など、プロセス全体をどのように実行するかについての豊富な知識が必要です。基本的に、それぞれの単体テストに合格する特定のコードが、最終結果に向かって連携して機能するかどうかを確認しています。
ソフトウェアテストやそれぞれのテストに関してはこちらをご覧ください。
>>ソフトウェアテストとは?種類や目的、重要な7原則を紹介のページへ
>>コンポーネント(単体、ユニット)テストとは?手法などを例で紹介のページへ
>>結合テストとは?目的や観点・種類・単体テストとの違いを解説のページへ
>>システムテスト(総合テスト)とは?その目的・観点・種類、実務で使える手順について解説のページへ
>>受け入れテスト(UAT)とは?実施の目的を観点別に紹介のページへ
テスト駆動開発のメリット
テスト駆動開発を行うメリットは、以下が代表的です。
・早い段階で不具合を検知できる
・要件/仕様を理解しやすくなる
・開発者の負担を軽減できる
それぞれ詳しく見てみましょう。
早い段階で不具合を検知できる
テスト駆動開発を実施すると、機能を実装するために必要な最低限のコードを作成していくため、実装がシンプルになります。シンプルであるがゆえに不具合が少なくなります。
また、本来QA段階で確認していたような部分なども、開発段階で不具合を多数発見し、修正していくことができます。早い段階で不具合を発見できるため、対処できるまでのリードタイムも短く、結果的に品質が上がっていくでしょう。
QAに関してはこちらもあわせてご覧ください。
>>ソフトウェアのQA(品質保証)とは?QAエンジニアの役割も合わせて解説のページへ
要件/仕様を理解しやすくなる
テストコードを書いていくためには、そのプロダクトの仕様を理解する必要があります。そのため、必然的に仕様への理解が深まっていくでしょう。また、仕様に適合しない点に気づくこともあり、本番のコードを書いているときに発見して1からやり直すような事態にはなりにくくなります。
開発者の負担を軽減できる
テスト駆動開発では、追加のコーディングやリファクタリングのたびにテストができるため、プログラムを壊していないか確認しやすくなります。プログラム変更は不具合が生じやすく、開発者にとって心理的負担がかかります。簡単にテストが実施できるようになることで、エンジニアも安心して作業することができ、心理的負担が下がっていきます。
テスト駆動開発のデメリット
テスト駆動開発を行うにあたって知っておくべきデメリットとしては以下があります。
・テストコードの実装・保守に時間がかかる
・慣れるまでに時間がかかる
・開発コストが大きくなる
なぜ、これらがデメリットとなりうるのかを解説します。
テストコードの実装・保守に時間がかかる
開発の仕様が変わることでテストコードを書き換える必要が出てくるため、メンテナンスが困難になる可能性もあるでしょう。仕様変更に伴ってテストケースを考える際、考慮する内容が増えたり、テストコード自体が増えたりすることがあります。
一方で、テストコードの実装に成功さえすれば、後の不具合の修正などの手間を削減できる可能性もあります。一見すると時間がかかるように見えますが、結果的には開発時間の短縮につながった事例も少なくありません。
慣れるまでに時間がかかる
いままで採用している開発手法から大きく変わる場合、やり方の変化に慣れるまで時間や労力が必要です。開発初期段階のコードを書く作業は、特に時間がかかる傾向があります。意図したとおりのテストコードを作るには、ある程度の経験と慣れが必要になってきます。
慣れるまでに時間や労力がかかりますが、それらを乗り越えた先に大きなメリットがあります。開発チーム内でテスト駆動開発を導入する目的や享受したいメリットをあらかじめ共有しておきましょう。
開発コストが大きくなる
上記のように、テスト駆動開発に慣れない影響で開発時間が膨らみ、コストが膨らんでいく可能性があります。特にテストコードを書くのに時間がかかるため、開発初期段階での負担が大きくなる傾向があるでしょう。
テスト駆動開発の経験者を中心に開発チームを組むなどして、時間的なロスがなくなるような形をとる必要があります。
テスト駆動開発のやり方
基本的なテスト駆動開発(TDD)のサイクルについて詳しく説明していきます。
1.失敗するテストコードを書く
2.成功するコードを書く
3.リファクタリングする
1.失敗するテストコードを書く
実装したい機能に対して実現するコードが書かれていない場合でも、その機能のテストコードから先に書いていきます。パスするテストではなく、“必ず失敗する”テストを書き、実行します。
実装する前にテストコードを実行するため、あたりまえですが失敗します。これが、「失敗するテストを書く」ということです。
この工程を“レッド(Red)”と呼ぶこともあります。由来は、テストツールを使うときにテストが失敗すると赤色でエラー表示されることから、“レッド(Red)”と呼ばれるようになったようです。
2.成功するコードを書く
失敗するテストコードが書けたら、機能の実装を行っていきます。前段階で失敗するテストを書いているため、それに伴って十分に仕様への理解も進んできていることでしょう。この段階で、初めて成功するコードの記述に入ります。
なお、ここでは完璧なコードを書くことを目的とはしていないため、テスト条件をすべてクリアするだけの“最小限“のコードを書けば問題ありません。最初の段階では固定値を返すなどから始まりますが、サイクルを繰り返すごとに徐々にロジックを追加していきましょう。
この工程では、あくまで要求を満たしたコードが正しくテストをパスできるかを確認する作業となり、チェックされる機能外に余分なコードを書かないことが期待されます。この工程を“グリーン(Green)”と言います。レッドとは反対でエラーがなくなることで、テストツールを使用しても緑色のバーが伸びていくことからきているようです。
3.リファクタリングする
前の工程で書いたコードを、テスト工程にパスできる状態を維持しつつきれいにしていく作業を行います。具体的には、以下のような作業です。
・抽象化
・変数名の修正
・メソッド化 など
この段階では、拡張性や保守性を改善する工程が該当します。
注意点として、「テストをパスしたからこのままで良い」ではないという点です。汚いコードをそのままにしておくと、後で収拾がつかなくなり、修正負担が増えて手に負えなくなっていきます。したがって、1つ前の工程を達成したら、その都度リファクタリングを行っていくようにしましょう。
リファクタリングが完了しても実装が済んでいなければ、最初の工程に戻って新しいテストコードを追加していき、実装が完了するまで繰り返していきます。
テスト駆動開発の失敗例
いくつかテスト駆動開発を行っていくうえでありがちな失敗例について、Matheus Rodrigues氏が自身のブログにて22項目のアンチパターンを解説しています。最大のアンチパターンは次の通りです。
嘘つき (The Liar)
すべてのユニットテストが、すべてのテストケースをパスしているように見えるが、意図されたテストがなされていないことが発見される。
よく見ると、テストすると主張しているものをテストしていません。クラス/メソッド名にちなんで名前をつけることができますが、実際には違う別のクラス/メソッドをテストしていることがあります。
そうならないために、テスト名を実装時に一致する名前に更新しなければなりません。
巨人(The Giant)
正当にオブジェクトをテストできているものの、1つのユニットテストが数千行からなる大量のテストケースを有している。これはテスト対象のオブジェクトが“God Object”であると示唆している。
“God Object”とは神のみぞ知る状態で何もかもを成すことができるレベルを指しており、1つのクラスにテストコードが異常にたくさん羅列されていたりします。クラスが大きい≒影響範囲も大きくなりメンテナンスも大変なものになります。オブジェクトを適切な粒度で分けて書いていかなければなりません。
実行に時間がかかるテスト(Slowpoke)
テスト駆動開発では「レッド->グリーン->リファクタリング」のサイクルをスムーズに回していく必要があるが、1つのObjectに時間がかかってしまうテストのケースがある。1つだけなら遅いテストかもしれないが、時間の経過とともにどんどん遅くなっていく。
テストが遅くなる原因としては、テストの過剰な設定です。テストへのセットアップが多すぎると、テストが遅くなるのです。また、テストのなかに隠れた依存関係があり、データベースにアクセスしようとしているために速度の低下を招くことがあります。
回避策としては、メソッドのセットアップ時に、より小さなメソッドに分割して管理することです。単体テストを高速に実行する必要がありますが、過剰なテストの設定のままではテストを高速に実行できません。隠れた依存関係を見つけたときも独自のクラスに分離し、視認できるようにします。
関連サービスについて
開発やテストの相談はSHIFTへおまかせ
テスト駆動開発は、慣れるまでが大変ではあるものの、多くのメリットをもった開発手法です。不具合の早期発見や開発者の負担軽減に役立つほか、コーディングの無駄も省くことができます。大きな開発であるほど大きな効果が得られるでしょう。積極的にテスト駆動開発を取り入れてみてはいかがでしょうか。
もし、自社での開発ができないなどのお悩みがあれば、SHIFTにご相談ください。テスト部分だけなどのご依頼から、開発の上流工程から納品までの一貫した対応も可能です。SHIFTが貴社の開発やテストの悩みを解決いたします。
ぜひSHIFTまでお問い合わせください。
>>ソフトウェアテスト・第三者検証のページへ
>>導入事例ページへ
>>料金についてページへ
>>お問い合わせページへ