アジャイル トランスペアレンシー ~アジャイル開発における透明性の確保について~

第5回 BDDとATDD

この記事を読むのに必要な時間:およそ 3 分

BDD の具体例(easyb)

それでは,具体的な BDD の例を見てみましょう。

Java で,以下のクラス図に示すようなテストを記載するとします。

図2

図2

JUnit で記載した場合の例は以下のとおりです。

import static junit.framework.Assert.*;
import org.junit.*;

public class CalculatorTest {

		:

	/**
	 * 割り算テスト
	 */
	@Test
	public final void testDiv() {
		Calculator cal = new Calculator(0);
		//0/10=0
		assertEquals(0, cal.div(10).getValue());
		//(0+60)/2/3=10
		assertEquals(10, cal.add(60).div(2).div(3).getValue());
	}

	/**
	 * 割り算テスト(異常系)
	 */
	@Test(expected = IllegalArgumentException.class)
	public final void testDivZero() {
		Calculator cal = new Calculator(0);
		cal.div(0);
	}

これを,Groovy を使った easyb で記述すると,このようになるでしょう。

import Calculator

scenario "10の値を持つ計算機オブジェクトに5を除算する", {
    given "値が10の計算機オブジェクトを生成する", {
        calculator = new Calculator(10)
    }
    when "計算機オブジェクトに5を除算する", {
        calculator.div(5)
    }
    then "計算機が保持する値は2である", {
        calculator.value.shouldEqual 2
    }
    and "計算機が保持する値は正である", {
    	ensure(calculator.plus)
    }
}
scenario "計算機オブジェクトを0で除算して例外を発生させる", {
    given "計算機オブジェクトを0で除算する", {
        var = {
        	(new Calculator(10)).div(0)
        }
    }
    then "例外が発生する", {
        ensureThrows(IllegalArgumentException) {
        	var.call()
        }
    }
}

あえて容易な題材に大量のコメントを入れていますが,ニュアンスは分りやすくなったと思います。記載内容は,テストでも振る舞いでも同じです。ですが,振る舞いの場合,シナリオ形式で書きますので,given, when, then のリズムで記述することが保証されます。この形式が徹底されることで,以下のように,ストーリーとして振る舞いを出力することが可能となります。

Stories Text
8 scenarios executed successfully.

  Story: calculator

	:

    scenario 10の値を持つ計算機オブジェクトに5を除算する
      given 値が10の計算機オブジェクトを生成する
      when 計算機オブジェクトに5を除算する
      then 計算機が保持する値は2である
      then 計算機が保持する値は正である

    scenario 計算機オブジェクトを0で除算して例外を発生させる
      given 計算機オブジェクトを0で除算する
      then 例外が発生する

振る舞いといっても,テストシナリオそのものです。そして,可読性は,普通にテストを書いていた場合と比べ,格段に改善します。

このように,振る舞い駆動開発では,記述のルールを強制することで,可読性の高いテストシナリオ(=振る舞い仕様)を,定めることが可能となります。

このようなDSLを用いて可読性を高めるツールは,その特性から,受け入れテスト(機能テスト)にのみ用いられるように考えられがちです。あらゆるレイヤーで,オブジェクトの振る舞いを明示する際に利用することが可能です。ただし,BDD においては,TDD同様に,あくまでも開発を駆動する設計作業として,DSLを用います。そして,振る舞い(=テスト)の記述は,開発者が実施します。

つまり,BDD のコンテキストにおいては,顧客と開発者の間の認知ギャップに対する対策にはなりきれていません。そこで,テスト駆動開発の考え方を,顧客が作成できる,機能テスト(=受け入れテスト)にまで広げようとする考え方があります。次にご紹介する受け入れテスト駆動開発(ATDD)です。

受け入れテストの位置づけ

TDD や BDD では,テスト作成と実装を交互に繰り返しながら,ソフトウェアを成長させてゆくやり方を取ります。そして,テストは,クラスないしはコンポーネント(意味上の一つのクラス群)に対しての単体テストレベルのテストが中心となります。それらのオブジェクトの使い方を明らかにすることが大きな目的の一つだからです。

一つ一つのオブジェクトの振る舞いについての透明性をあげることは,開発者間の意思疎通不全を解消するのに効果はありますが,顧客と開発者の間の認識違いを解消することはできません。

顧客に分かりやすい言葉でソフトウェアを表して,顧客と開発者の間の認識違いを解消する必要があります。以前,ストーリーによる仕様の表現で,この問題に対処する方法をご説明しました。また細かくイテレーションを切って,簡易リリースを繰り返す方法もご説明しました。それらを補完し,この問題を解消する考え方が,受け入れテストの早期実施とその自動化です。

各イテレーションの最後に,受け入れテストをしてもらい,成果物を確認してもらいます。その受け入れテストは自動化しておいて,次回イテレーションの終わりには再び流してデグレードしていないかどうか確認してゆきます。そうすることで,開発期間最後のイテレーションにおけるテストをより軽量化することが可能となります。

自動化ツールの多くは,ユーザーの操作を記録して再実行させることが可能です。その為,イテレーションの最後に受け入れテストの実装と,その自動化の作業を組み込むことになります。

ですが,その考え方をさらに進めて,テスト駆動開発のレベルで受け入れテストの考え方を組み込んでしまうという ATDD のアプローチがあります。

受け入れテスト駆動開発

受け入れテスト駆動開発(Acceptance TDD)とは,TDD の開発を駆動するテストとして,受け入れテスト(機能テスト)を使おうとする試みです。こちらも テスト駆動開発を行うという意味では変わらないのですが,テストの粒度をより受け入れテストに近いものとして実装していく点に違いがあります。開発者が TDD のリズムを刻みながら実装を進めるものですから,受け入れテストとは粒度は異なります。

とはいえ,単体テストレベルのTDD にくらべ,テストの粒度は大きくなりますので,実装と並行してテストを進化させるようなリズミカルなコーディングスタイルにはなりません。ある程度の受け入れテストを事前に作成しておいて,それを通過するように実装を進めるといった作業になり,レッドの状態(テストが通過しない状態)は,相対的に長くなります。

しかし,事前に自動化されたテストを実行可能にすることは,受け入れレベルのテストだと困難なことがあります。ツールで,実際の動作を記録して自動化した方が楽なケースが多いからです。

  • 誰がテストケースを書くのか?
  • どのタイミングでテストコードを実装するのか?
  • ユニットレベルのTDDとの住み分けをどうするのか?

などなど工夫の余地は大きいですが,受け入れテストを可能な限り前倒しで用意し,自動化しておくことのメリットは非常に大きいです。

まとめ

テストは,アジャイル開発においては,品質を担保するのみならず,開発を促進する潤滑油であり,開発の透明性を高める情報共有ツールでもあります。テストにより開発を駆動するという考え方を共有し,その効果を最大限に発揮できるよう創意工夫をこらすことが,アジャイル開発においては大切です。

著者プロフィール

設楽秀輔(したらしゅうすけ)

1994年,慶応義塾大学経済学部卒業。エンターテインメント系企業にて経営管理を経験後,システムインテグレータとして金融アプリケーションなどのソフトウェア開発に従事。2007年,株式会社テクノロジックアートに入社し,現在に至る。

テクノロジックアートアジャイル開発グループグループリーダー。認定スクラムマスター。会計士補。