テスト駆動開発/振る舞い駆動開発を始めるための基礎知識

@IT

TDDの概要と進め方、目的と効果、歴史、さまざまな手法への展開、課題に加え、BDDの概要と種類、 重視される考え方などを解説する。

[井芹洋輝STAR(テスト自動化研究会)]

連載目次

 

2000年代初期に開発手法として確立された「テスト駆動開発」(Test Driven Development、以下「TDD」)は、その後10年もの間で普及が進み、今や珍しくない開発スタイルの1つとなっています。国内でも「アジャイルアカデミー」「TDD Boot Camp」などによる推進・普及活動が各地で活発化し、認知が広がってきました。

なおTDDは誕生からこれまでの間に、さまざまな工夫や実践上のノウハウが提唱されてきました。またTDDの普及に影響を受け、他のさまざまな「テストファースト」手法も台頭してきています。

本稿では、そうしたTDDの発展や、振る舞い駆動開発(Behavior Driven Development、以下「BDD」)など他のテストファースト手法への展開についても解説します。

※編集部注:ソフトウェアの「テスト」そのものの概要や種類について知りたい方は記事「JUnitとEclipseを使って学ぶ、“テスト”の常識」を参照してください。

テスト駆動開発(TDD)とは

TDDとはテストファーストによる追加・変更と、リファクタリングによる設計改善の2つの活動を超短期で繰り返して開発を進めていく手法です。この手順は「RED」(テストを失敗させた状態)、「GREEN」(テストを成功させた状態)、「REFACTOR」(リファクタリングをしている段階)の3ステップを高速に回すサイクルで表現されます。

tdd_bdd1_01.jpg

5ステップで分かるTDDの進め方

TDDの進め方について、簡単な流れを解説していきます。今回は例として、うるう年判定関数「isLeapYear()」を題材に、テスティングフレームワーク「Google Test」を使ってC++のコードを実装する流れを解説します。

【1】失敗するテストコードを書く

最初に、失敗するテストコードを記述します。また、テストを実行可能にするために、テスト対象コードはテストを失敗させるように仮実装します。

  1. TEST(isLeapYearTest, 4で割り切れる年はうるう年と判定する)
  2. {
  3. EXPECT_EQ(true, isLeapYear(8));
  4. }
テストコード
  1. bool isLeapYear(int year)
  2. {
  3. return false;
  4. }
テスト対象コード

【2】テストを失敗から成功に変化させるようにテスト対象コードを書く

テスト失敗を確認したら、失敗を成功に変えるために、下記のようにテスト対象コードを修正します。

  1. bool isLeapYear(int year)
  2. {
  3. if (year % 4 == 0) {
  4. return true;
  5. }
  6. return false;
  7. }
テスト対象コード

【3】テストを失敗させるテストコードを追加する

テスト成功を確認できたら、新たなコードを書くために、失敗するテストを追加します。

  1. TEST(isLeapYearTest, 4で割り切れる年はうるう年と判定する)
  2. {
  3. EXPECT_EQ(true, isLeapYear(8));
  4. }
  5. TEST(isLeapYearTest, 100で割り切れる年はうるう年でないと判定する)
  6. {
  7. EXPECT_EQ(false, isLeapYear(200));
  8. }
テストコード

【4】テストを失敗から成功に変化させるようにテスト対象コードを書く

テスト失敗を確認したら、下記のようにテスト対象コードを追加・変更して、テストを失敗から成功に変化させます。

  1. bool isLeapYear(int year)
  2. {
  3. if (year % 4 == 0) {
  4. if (year % 100 == 0) {
  5. return false;
  6. }
  7. return true;
  8. }
  9. return false;
  10. }
テスト対象コード

【5】テストを成功状態にしたままリファクタリングする

コードのネストが汚くなってきたため、ここで「リファクタリング」を実施します。リファクタリングでは、テストが成功していることを確認した後、下記のようにコードをきれいにします。作業完了後、テストを再実行してテストが成功していることを確認します。

  1. bool isLeapYear(int year)
  2. {
  3. if ((year % 4 == 0) && (year % 100 != 0)) {
  4. return true;
  5. }
  6. return false;
  7. }
テスト対象コード

このように、まずテストを失敗させ、次にそれを成功させるようにテスト対象を追加変更する活動と、きりの良いタイミングでリファクタリングを細かく繰り返していくのが、TDDの基本的な流れです。

TDDの目的と効果

TDDの主な目的としては、軽快なフィードバックの確保きれいで動くコードの確保などによる、開発の改善が挙げられています。

TDDの効果としては、例えば組織として展開した場合、実行工数を15%から25%増加させる代わりに、欠陥密度を4割から9割低下させてデバッグや手戻り工数を減少させ、トータルの総工数を削減するといった事例が報告されています(参考『Realizing quality improvement through test driven development: results and experiences of four industrial teams.』Nachiappan Nagappan, E. Michael Maximilien, Thirumalesh Bhat and Laurie Williams(2008))。

TDDの歴史

テストファーストはXP以前の1980年代に提唱

TDDはテストファーストに基づく開発手法です。テストファーストについては、プログラミング手法「XP(eXtreme Programing)」の実践手法の1つとしての「テストファーストプログラミング」が著名ですが、その基本形はXPが生まれる前の1980年代ごろから提唱されていました(ただし、テストを最初に書くというアイデアは、1950年代から見られるそうです(参考『Ten Years Of Test Driven Development』))。

xUnitファミリの始祖「SUnit」

そこから、プログラミング言語「Smalltalk」のコミュニティを中心に具体的なツールや手法が整備されていきます。

具体的には、ユニットテストフレームワーク「xUnit」ファミリの始祖となる「SUnit」(Smalltalk向けテスティングフレームワーク)の開発、テストを活用したリファクタリングの考え方やTDDの原型となるプログラミング手法の確立などが、この時期に行われました。

tdd_bdd1_05.jpg

「TDDの原典」の登場とTDDの普及

tdd_bdd1_08.jpg ケント・ベック氏(記事「XPと宮本武蔵の勝利への執念、ケント・ベック」より引用)

そして、それら蓄積に基づいて2002年に「TDDの原典」と呼ばれることとなる『Test Driven Development By Example』(邦題『テスト駆動開発入門』ピアソンエデュケーション刊)がKent Beck(ケント・ベック)氏によって執筆されました。この書籍はRED、GREEN、REFACTORのサイクルを回す具体的な手順や、「Fake It」「Assert First」などTDDを構成するパターンを体系的に整理して解説しており、国内外問わず、TDDの普及を支えるものとなりました。

ちなみに初期のTDD(例えば『Test Driven Development By Example』)は、適用対象や適応範囲を絞らず、自動テストを使った「結合テスト」なども範囲に含めていました。ただ、実践・説明のしやすさやツールの充実度などから、TDDはプログラミング手法として認知・普及していくことになります。

受け入れテスト駆動開発(ATDD)とは

なお、プログラミング手法としてのTDDは、ユーザー価値の向上やユーザーとのコラボレーションといった、当初のテストファーストの役割に対応しなくなっています。そうしたものを補完するものとして、テストファーストプログラミングにおいては、「受け入れテスト駆動開発」(Acceptance Test Driven Development、以下「ATDD」)が提唱されています。

Be the first to comment

Leave a Reply

Your email address will not be published.


*