次の方法で共有


BDD 入門

SpecFlow と WatiN によるビヘイビア駆動開発

Brandon Satrom

サンプル コードのダウンロード

自動単体テストは、ソフトウェア開発の現場でますます広く実施されるようになり、さまざまなテストファースト手法の導入も進んでいます。どのテストファースト手法にもそれぞれ、開発チームにとってメリットもあれば課題もあります。しかし、いずれも "設計としてのテスト" という考え方の確立を目指しています。

ただし、テストファースト世代でも、ユーザーの行動 (ビヘイビア) を表現する方法については、システムの言語 (ユーザーの言語とはかけ離れた言語) で書かれた単体テストを利用する方法が主流でした。この傾向が、ビヘイビア駆動開発 (BDD) 手法の登場で、大きく変わろうとしています。BDD 手法を使用すると、実装するシステムとのつながりを維持しながら、ビジネスに使用する言語で自動テストを作成できます。

もちろん、これまでも、開発プロセスに BDD を導入するためのツールが多数作成されてきました。Ruby の Cucumber や、Microsoft .NET Framework 用の SpecFlow や WatiN などがこの代表的なツールです。SpecFlow は Visual Studio 内での仕様の作成と実施をサポートし、WatiN はブラウザーを使用して、エンド ツー エンドの自動システム テストを実行できるようにします。

今回の記事では、BDD の概要を簡単に紹介し、単体レベルの実装につながる機能レベルのテストによって、BDD サイクルに従来のテスト駆動開発 (TDD) サイクルがどのように組み込まれるかを説明します。テストファースト手法の基礎について説明し、SpecFlow と WatiN を紹介してから、この 2 つのツールと MSTest を併用して BDD をプロジェクトに実装する方法を例示します。

自動テストの簡単な歴史

アジャイル ソフトウェアの趨勢から生まれた最も価値ある実践手法の 1 つが、自動化されたテストファースト開発のスタイルで、これを通常、テスト駆動開発 (TDD) と呼びます。TDD の基本理念は、テストの作成を、検証と回帰を目的とするだけでなく、設計や開発のガイダンスとなることも目的とするというものです。また、必要な機能単位を示すこともテストの目的で、そのテストを使用してその機能を提供するためだけに必要なコードのみを作成します。したがって、新機能を実装する作業は、まず、失敗することを目的とするテストを考え、想定する結果を記述することです (図 1 参照)。

image: The Test-Driven Development Cycle

図 1 テスト駆動開発サイクル

多くの開発者や開発チームが、TDD により大きな成果を挙げています。ただし、成果が得られていないチームもあり、その場合は特にテスト量が増え、テストの柔軟性が失われ始めて、経時的なプロセス管理に苦労しています。また、TDD を導入する方法がわからない場合や、TDD の導入は簡単にできても、納期が近くなり、膨大なバックログが現れるとおざなりになってしまうこともあります。さらに、TDD に関心のある多くの開発者が、組織内で TDD に対する抵抗にあっています。これは、"テスト" という言葉が別のチームの職能を暗に示していること、または TDD では追加コードが多すぎてプロジェクトの進行を遅くする結果になるという誤解が原因です。

Steve Freeman と Nat Pryce は著書『Growing Object-Oriented Software, Guided by Tests』(Addison-Wesley Professional、2009 年) の中で、“従来の” TDD では、真のテストファースト開発のメリットが一部失われると記しています。

「アプリケーションに含まれるクラスの単体テストを作成することから、TDD プロセスに着手しがちです。これは、まったくテストを用意しないよりもましですし、この方法では、だれもがわかっていても回避が難しい、基本プログラミングの誤りを検出できます。...ただし、単体テストしか実施しないプロジェクトは、TDD プロセスの大切なメリットを逃していることになります。品質が良く、単体テストが十分に実施されていても、どこからも呼び出されていなかったり、システムの他のコードと統合できず、書き直しを余儀なくされるコードがあるプロジェクトというものを私たちは実際に見てきました。」

2006 年に Dan North は雑誌『Better Software』(blog.dannorth.net/introducing-bdd、英語) において、このような課題の多くを記録しています。この記事で North は、テストの最前線でそれまでの 3 年間にわたり採用されてきた一連の実践手法について説明しています。定義上は TDD ではありますが、それらの実践手法から、より分析を中心とするテストが採用されるようになりました。North は、この新しい実践手法へのシフトを表すために、"ビヘイビア駆動開発" という用語を生み出しました。

BDD を適用する一般的な手法の 1 つは、受け入れテスト (実行可能仕様) を通じて、テストを作成する目的とプロセスとを結び付けて、TDD の拡張を図る手法です。各仕様は、開発サイクルのエントリ ポイントとなり、ユーザーの観点から段階的にシステムの動作 (ビヘイビア) を記述します。動作を記述したら、開発者はその仕様と既存の TDD プロセスを使用して、必要最低限の運用コードを実装し、そのテストが合格するシナリオを作成します (図 2 参照)。

image: The Behavior-Driven Development Cycle

図 2 ビヘイビア駆動開発サイクル

設計に入るタイミング

BDD は、TDD に代わるものではなく、TDD のスーパーセットであるという見方が大半です。最大の違いは、初期設計とテストの作成に重点を置くことです。単体テストまたはオブジェクトのテストに重点を置くのではなく、TDD と同様、ユーザーの目的と、その目的の実現に向けて実行する手順に重点を置きます。BDD では、小さな単体テストから開始しないため、以前ほど細かい用法や設計の詳細について考えることがなくなります。代わりに、システムの妥当性を証明する実行可能仕様を作成します。単体テストも作成しますが、BDD では、実装すべき機能の完全な記述から始めるという、外から内へのアプローチが推奨されます。

この違いを示す例を見てみましょう。従来の TDD の実践手法では、図 3 のようなテストを作成して、CustomersController の Create メソッドを評価していました。

図 3 顧客レコード作成の単体テスト

[TestMethod]
public void PostCreateShouldSaveCustomerAndReturnDetailsView() {
  var customersController = new CustomersController();
  var customer = new Customer {
    Name = "Hugo Reyes",
    Email = "hreyes@dharmainitiative.com",
    Phone = "720-123-5477" 
  };

  var result = customersController.Create(customer) as ViewResult;

  Assert.IsNotNull(result);
  Assert.AreEqual("Details", result.ViewName);
  Assert.IsInstanceOfType(result.ViewData.Model, typeof(Customer));

  customer = result.ViewData.Model as Customer;
  Assert.IsNotNull(customer);
  Assert.IsTrue(customer.Id > 0);
}

TDD では、このようなテストを最初に作成するのが一般的です。CustomersController オブジェクトに想定する動作を設定して、このオブジェクトへのパブリック API を設計します。BDD でもこのようなテストを作成しますが、最初に作成するテストではありません。BDD では、図 4 のようなコードを作成して、機能レベルの仕様に重点を置くようにします。次に、このシナリオをガイドにして、このシナリオが問題なく機能するために必要な各コード単位を実装します。

図 4 機能レベルの仕様

Feature: Create a new customer
  In order to improve customer service and visibility
  As a site administrator
  I want to be able to create, view and manage customer records

Scenario: Create a basic customer record
  Given I am logged into the site as an administrator
  When I click the "Create New Customer" link
  And I enter the following information
    | Field | Value                       |
    | Name  | Hugo Reyes                  |
    | Email | hreyes@dharmainitiative.com |
    | Phone | 720-123-5477                |
  And I click the "Create" button
  Then I should see the following details on the screen:
    | Value                       |
    | Hugo Reyes                  |
    | hreyes@dharmainitiative.com |
    | 720-123-5477                |

これは、図 2 の外側のループを表し、失敗を想定する受け入れテストです。このテストを作成し、失敗したら、図 2 の内側の TDD ループに従って、機能に各シナリオの各ステップを実装します。図 3 の CustomersController の場合は、機能内の適切なステップに達した時点、ただし、CustomersController のロジックを実装する前に、このテストを作成します。

BDD と自動テスト

当初から、BDD コミュニティは、単体テストでは標準となって久しい自動テストを、受け入れテストでも同じレベルで実現することを模索してきました。有名な例としては Cucumber (cukes.info、英語) が挙げられます。これは、Ruby ベースのテストツールで、"ビジネス (人) が読み取れる、地域固有の言語" で作成された機能レベルの受け入れテストの作成を主に扱います。

Cucumber テストは、各機能ファイルにはユーザー ストーリー構文を、各シナリオには Given、When、Then (GWT) 構文を使用して作成します (ユーザー ストーリー構文の詳細については、c2.com/cgi/wiki?UserStory (英語) を参照してください)。GWT では、シナリオの現在コンテキスト (Given)、テストの一環として実行されるアクション (When)、および想定される観察可能化結果 (Then) を記述します。図 4 の機能は、この構文の例です。

Cucumber では、ユーザーが読み取れる機能ファイルが解析されて、各シナリオのステップが、問題のシステムのパブリック インターフェイスを評価する Ruby コードに対応付けられ、そのステップの合否が判定されます。

近年では、シナリオを自動テストとして使用できるようにする革新技術が、.NET Framework のエコシステムにも組み入れられるようになりました。現在では、Cucumber が使用するのと同じ構造化英語構文を使用して仕様書を作成でき、コードを評価するテストとしてその仕様書を使用できるツールが利用できるようになっています。SpecFlow (specflow.org、英語)、Cuke4Nuke (github.com/richardlawrence/Cuke4Nuke、英語) をはじめとする BDD テスト ツールでは、プロセスの最初に実行可能仕様を作成し、その仕様を利用して、機能を構築し、開発およびテスト プロセスに直接結び付いた、ドキュメント化された機能を作成できます。

SpecFlow と WatiN を使用する

今回は、SpecFlow を使用してモデル ビュー コントローラー (MVC: Model-View-Controller) アプリケーションをテストします。SpecFlow を使用するには、まず、ダウンロードしてインストールします。SpecFlow をインストールしたら、単体テスト プロジェクトを備えた新しい ASP.NET MVC アプリケーションを作成します。ここでは、単体テスト プロジェクトに単体テスト以外 (コントローラー テスト、リポジトリ テストなど) は含めず、SpecFlow テスト用に AcceptanceTests テスト プロジェクトを別に作成します。

AcceptanceTests プロジェクトを追加し、TechTalk.SpecFlow アセンブリへの参照を追加したら、SpecFlow によってインストール時に作成される [Add] (追加) の [New Item] (新しいアイテム) のテンプレートを使用して新しい機能を追加し、これに CreateCustomer.feature という名前を付けます。

ファイルは .feature という拡張子を付けて作成します。また、SpecFlow は統合ツールなので、Visual Studio ではこれがサポート対象ファイルとして認識されます。さらに、機能ファイルには関連する .cs 分離コード ファイルもあります。.feature ファイルを保存するたびに、SpecFlow はファイルを解析して、そのファイル内のテキストをテスト フィクスチャーに変換します。関連する .cs ファイル内のコードは、このテスト フィクスチャーを表します。これは、テスト スイートを実行するたびに、実際に実行されるコードです。

既定では、SpecFlow は NUnit をテスト実行ツールに使用しますが、簡単な構成の変更で、MSTest もサポートします。それには、app.config ファイルをテスト プロジェクトに追加して、次の要素を追加するだけです。

<configSections>
  <section name="specFlow"
    type="TechTalk.SpecFlow.Configuration.ConfigurationSectionHandler, TechTalk.SpecFlow"/>
</configSections>
<specFlow>
  <unitTestProvider name="MsTest" />
</specFlow>

最初の受け入れテスト

新しい機能を作成するときに、SpecFlow は、機能の記述に使用する構文を表す既定のテキストをそのファイルに追加します。CreateCustomer.feature ファイル内の既定のテキストを、図 4 のテキストに置き換えます。

各機能ファイルには、2 つの部分があります。最初の部分は、ファイル上部にある機能の名前と説明です。ここではユーザー ストーリー構文を使用して、ユーザーの役割、ユーザーの目的、およびシステムでその目的を達成するためにユーザーが実行できる作業の種類を記述します。このセクションは、SpecFlow によるテストの自動生成に必要ですが、セクションの内容自体はそれらのテストでは使用されません。

各機能ファイルの 2 つ目の部分は、1 つ以上のシナリオから構成されます。各シナリオを基に関連する .feature.cs ファイルにテスト メソッドが生成され (図 5 参照)、シナリオ内の各ステップが SpecFlow のテスト実行ツールに渡されます。テスト実行ツールは、ステップ定義ファイルという SpecFlow ファイル内のエントリに対してステップの RegEx ベースの照合を行います。

図 5 SpecFlow によって生成されるテスト メソッド

public virtual void CreateABasicCustomerRecord() {
  TechTalk.SpecFlow.ScenarioInfo scenarioInfo = 
    new TechTalk.SpecFlow.ScenarioInfo(
    "Create a basic customer record", ((string[])(null)));

  this.ScenarioSetup(scenarioInfo);
  testRunner.Given(
    "I am logged into the site as an administrator");
  testRunner.When("I click the \"Create New Customer\" link");

  TechTalk.SpecFlow.Table table1 = 
    new TechTalk.SpecFlow.Table(new string[] {
    "Field", "Value"});
  table1.AddRow(new string[] {
    "Name", "Hugo Reyesv"});
  table1.AddRow(new string[] {
    "Email", "hreyes@dharmainitiative.com"});
  table1.AddRow(new string[] {
    "Phone", "720-123-5477"});

  testRunner.And("I enter the following information", 
    ((string)(null)), table1);
  testRunner.And("I click the \"Create\" button");

  TechTalk.SpecFlow.Table table2 = 
   new TechTalk.SpecFlow.Table(new string[] {
  "Value"});
  table2.AddRow(new string[] {
    "Hugo Reyes"});
  table2.AddRow(new string[] {
    "hreyes@dharmainitiative.com"});
  table2.AddRow(new string[] {
    "720-123-5477"});
  testRunner.Then("I should see the following details on screen:", 
    ((string)(null)), table2);
  testRunner.CollectScenarioErrors();
}

最初の機能を定義したら、Ctrl キーを押しながら R キーと T キーを押して、SpecFlow テストを実行します。CreateCustomer テストは、SpecFlow がテストの最初のステップに対応するステップ定義を発見できないため、結果不確定として失敗します (図 6 参照)。想定結果は、分離コード ファイルではなく、実際の .feature ファイルにレポートされます。

image: SpecFlow Cannot Find a Step Definition

図 6 SpecFlow によりステップ定義を発見できない

ステップ定義ファイルを作成していないため、この例外は想定どおりの結果です。例外のダイアログ ボックスで [OK] をクリックし、Visual Studio のテスト結果ウィンドウで CreateABasicCustomerRecord を探します。対応するステップが見つからない場合、SpecFlow は機能ファイルを使用して、ステップ定義ファイルに必要なコードを生成します。このコードをコピーして、ステップの実装に使用できます。

AcceptanceTests プロジェクトに、SpecFlow Step Definition テンプレートを使用してステップ定義ファイルを作成し、CreateCustomer.cs という名前にします。その後、SpecFlow からの出力をクラスにコピーします。各メソッドには、そのメソッドを Given、When、または Then ステップに指定する SpecFlow 属性が設定されていて、機能ファイルのステップとのメソッドの対応付けに使用される RegEx が用意されています。

ブラウザー テストを実行するために WatiN を統合する

BDD を使用する目的には、エンド ツー エンド システムの機能を可能な限り評価する、自動テスト スイートを作成することも含まれます。ここでは、ASP.NET MVC アプリケーションを作成しているため、Web ブラウザーがサイトと通信するスクリプトの作成に便利なツールを使用できます。

このようなツールの 1 つが WatiN です。これは、Web ブラウザーのテストを自動化するオープン ソースのライブラリです。WatiN は watin.sourceforge.net (英語) からダウンロードでき、WatiN.Core への参照を受け入れテスト プロジェクトに追加してこれを使用することができます。

WatiN は基本的に、ブラウザー オブジェクトを利用して操作します。このオブジェクトは、選択したブラウザーに応じて IE() または FireFox() になり、インストールされているブラウザーのインスタンスを制御するパブリック インターフェイスを提供します。1 つのシナリオで複数のステップをブラウザーから順次実行する必要があるため、ステップ定義クラスのステップ間で同じブラウザー オブジェクトを渡す手段が必要です。通常、これを処理する場合、WebBrowser 静的クラスを AcceptanceTests プロジェクトの一部として作成し、そのクラスを使用して WatiN IE オブジェクトと、SpecFlow がシナリオのステップ間の状態の格納に使用する ScenarioContext を操作します。

public static class WebBrowser {
  public static IE Current {
    get {
      if (!ScenarioContext.Current.ContainsKey("browser"))
        ScenarioContext.Current["browser"] = new IE();
      return ScenarioContext.Current["browser"] as IE;
    }
  }
}

CreateCustomer.cs に実装する必要がある最初のステップは、Given ステップです。このステップでは、管理者としてユーザーをサイトにログインすることで、テストを開始します。

[Given(@"I am logged into the site as an administrator")]
public void GivenIAmLoggedIntoTheSiteAsAnAdministrator() {
  WebBrowser.Current.GoTo(http://localhost:24613/Account/LogOn);

  WebBrowser.Current.TextField(Find.ByName("UserName")).TypeText("admin");
  WebBrowser.Current.TextField(Find.ByName("Password")).TypeText("pass123");
  WebBrowser.Current.Button(Find.ByValue("Log On")).Click();

  Assert.IsTrue(WebBrowser.Current.Link(Find.ByText("Log Off")).Exists);
}

前述のとおり、シナリオの Given の部分は、現在のテストのコンテキストを設定するセクションです。WatiN を使用すると、テストを実行して、ブラウザーを操作し、このステップを実装できます。

このステップでは、WatiN を使用して Internet Explorer を開き、サイトの [Log On] (ログオン) ページに移動し、[User name] (ユーザー名) ボックスと [Password] (パスワード) ボックスに入力して、この画面の [Log On] (ログオン) ボタンをクリックします。テストを再度実行すると、Internet Explorer が自動的に開き、WatiN がサイトと通信して、リンクをクリックし、テキストを入力するようすを観察できます (図 7 参照)。

image: The Browser on Autopilot with WatiN

図 7 WatiN による自動操作中のブラウザー

今回は Given ステップがテストに合格し、機能の実装に 1 歩近づきました。SpecFlow は、最初の When ステップがまだ実装されていないため、今度はこのステップで失敗します。次のコードを使用すると、このステップを実装できます。

[When("I click the \" (.*)\" link")]
public void WhenIClickALinkNamed(string linkName) {
  var link = WebBrowser.Link(Find.ByText(linkName));

  if (!link.Exists)
    Assert.Fail(string.Format(
      "Could not find {0} link on the page", linkName));

  link.Click();
}

この時点でテストを再度実行すると、WatiN がページ上に [Create New Customer] (新規顧客の作成) というテキストのリンクを発見できないため、テストは失敗します。このテキストのリンクをホームページに追加するだけで、次のステップはテストに合格します。

パターンが見えてきましたか。SpecFlow では、テストファースト開発手法の基本である Red-Green-Refactor と同じ方法論を推奨しています。機能の各ステップの詳細度が、事実上、余分な実装を排除する働きをし、ステップがテストに合格するために必要な機能のみが実装されるようになっています。

しかし、BDD プロセス内の TDD はどうでしょうか。この時点では、ページ レベルでしか作業をしていませんが、実際にはまだ顧客レコードを作成する機能を実装する必要があります。説明を簡単にするため、残りのステップをここで実装しましょう (図 8 参照)。

図 8 ステップ定義の残りのステップ

[When(@"I enter the following information")]
public void WhenIEnterTheFollowingInformation(Table table) {
  foreach (var tableRow in table.Rows) {
    var field = WebBrowser.TextField(
      Find.ByName(tableRow["Field"]));

    if (!field.Exists)
      Assert.Fail(string.Format(
        "Could not find {0} field on the page", field));
    field.TypeText(tableRow["Value"]);
  }
}

[When("I click the \"(.*)\" button")]
public void WhenIClickAButtonWithValue(string buttonValue) {
  var button = WebBrowser.Button(Find.ByValue(buttonValue));

  if (!button.Exists)
    Assert.Fail(string.Format(
      "Could not find {0} button on the page", buttonValue));

  button.Click();
}

[Then(@"I should see the following details on the screen:")]
public void ThenIShouldSeeTheFollowingDetailsOnTheScreen(
  Table table) {
  foreach (var tableRow in table.Rows) {
    var value = tableRow["Value"];

    Assert.IsTrue(WebBrowser.ContainsText(value),
      string.Format(
        "Could not find text {0} on the page", value));
  }
}

テストを再実行すると、今度は、顧客情報を入力するページがないためにテストが失敗します。顧客レコードを作成できるようにするには、[Create Customer View] (顧客ビューの作成) ページが必要です。ASP.NET MVC でこのようなビューを提供するには、そのビューを提供する CustomersController が必要です。そこで新しいコードが必要になります。つまり、BDD である外側のループから外れて、TDD である内側のループに取り掛かります (再び図 2 を参照してください)。

それには、まず、失敗を想定した単体テストを作成します。

単体テストを作成して、ステップを実装する

UnitTest プロジェクトに CustomerControllersTests テスト クラスを作成したら、CustomersController によって公開される機能を評価するテスト メソッドを作成します。具体的には、Controller の新しいインスタンスを作成して、その Create メソッドを呼び出し、適切なビューとモデルが返されるようにします。

[TestMethod]
public void GetCreateShouldReturnCustomerView() {
  var customersController = new CustomersController();
  var result = customersController.Create() as ViewResult;

  Assert.AreEqual("Create", result.ViewName);
  Assert.IsInstanceOfType(
    result.ViewData.Model, typeof(Customer));
}

CustomersController またはその Create メソッドを作成していないため、このコードはまだコンパイルされません。CustomersController とCreate メソッドを作成すると、コードがコンパイルされますが、その時点ではテストに失敗します。これは、次の段階として望ましい結果です。Create メソッドを完全に作成すると、テストに合格します。

public ActionResult Create() {
  return View("Create", new Customer());
}

SpecFlow テストを再実行すると、もう少し先に進めますが、機能はやはりテストに合格しません。今度は、Create.aspx ビュー ページがないために失敗します。これを、機能によって指定されている適切なフィールドと併せて追加すると、機能の完成へともう一歩近づきます。

この作成機能を実装する、外から内へのプロセスは、図 9 のように表されます。

image: Scenario-to-Unit Test Process

図 9 シナリオから単体テストを作成するプロセス

上記と同じステップはこのプロセス内で頻出するため、特に、ヘルパー ステップ (リンクやボタンのクリック、フォーム入力など) が AcceptanceTests プロジェクトに実装されて、各シナリオの主要機能のテストが進むに従って、これらのステップの処理速度は大幅に向上します。

次に、このサンプル機能では、有効な Create View から、適切なフォーム フィールドの入力が行われ、フォームの送信が試みられます。ここまでくれば、次に何が起きるかは想像できるでしょう。テストは、顧客レコードの保存に必要なロジックが用意されていないため、失敗します。

前と同じプロセスに従って、前に図 3 で紹介した単体テスト コードを使用し、テストを作成します。顧客オブジェクトを受け取る空の Create メソッドを追加してこのテストがコンパイルされるようにしたら、テストが失敗するのを確認し、次のように Create メソッドを完成します。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Customer customer) {
  _repository.Create(customer);

  return View("Details", customer);
}

この Controller はコントローラーにすぎず、顧客レコードの実際の作成は、データ保存メカニズムに対応できる Repository オブジェクトで処理されます。話が複雑にならないように、その実装についてはこの記事では割愛しますが、実際のシナリオでは、顧客レコードを保存するリポジトリが必要な場合は、単体テストの別のサブループが開始されます。連携するオブジェクトにアクセスする必要があっても、そのオブジェクトが存在していないか、必要な機能を提供しない場合は、各自の機能とコントローラーに採用したものと同じ単体テスト ループを採用します。

Create メソッドを実装し、機能するリポジトリを用意できたら、詳細ビューを作成する必要があります。詳細ビューは、新しい顧客レコードを取得して、ページにそのレコードを表示します。詳細ビューを作成できたら、SpecFlow を再度実行できます。これで、多数の TDD ループとサブループの処理を経て、ついに、システムのエンド ツー エンドの機能要件を満たした、テストに合格する機能が完成しました。

お疲れさまでした。これで、受け入れテストと、システムを拡張して新機能を追加しても新しい機能が引き続き動作することを保証する包括的な一連の単体テストを使用して、エンド ツー エンドの機能が実装されました。

リファクタリングについてひと言

UnitTests プロジェクトに単体テストを作成する際は、できれば、テストを作成するたびにリファクタリングを実行してください。合格する単体テストから合格する受け入れテストへと逆にテストを進める場合も、同じプロセスに従って、リファクタリングの機会を探し、各機能とそれに続くすべての機能の実装を調整します。

AcceptanceTests プロジェクトでも、コードのリファクタリングをできる機会に目を光らせてください。特に Given ステップなどは、複数の機能間で同じステップが繰り返される傾向があります。SpecFlow を使用すると、このようなステップを機能ごとに個別のステップ定義ファイル (例: LogInSteps.cs) にまとめることができます。このようにすることで、メインのステップ定義ファイルを簡潔で、指定している一意のシナリオに特化した状態に保つことができます。

設計と開発に重点を置くことが BDD の主眼です。オブジェクトから機能へ重点を移すことで、開発者自身も開発チームも、システムのユーザーの観点から設計することができます。機能設計が単体設計になるため、必ず機能を念頭にテストを作成し、個々のステップまたはタスクに従ってテストを作成するようにします。

他の実践方法や原理と同様、BDD がワークフローに馴染むまでには時間がかかります。何か利用できるツールを使用して、ご自身で BDD を試し、時間をかけて BDD の機能を確認されることをお勧めします。このような形で開発を行う場合は、BDD において確認することが推奨されている検討事項に留意してください。常に一度立ち止まり、実践方法と実践過程を改善する方法を模索し、他の開発者と協力して改善のアイデアを練ってください。どのようなツールを使用するとしても、BDD を学ぶことで、ご自身のソフトウェア開発プラクティスに新たな価値と視点が加わることを願っています。

Brandon Satrom は、テキサス州オースティンでマイクロソフトの開発者エバンジェリストとして活躍しています。彼のブログは userinexperience.com (英語) です。Twitter での連絡先は @BrandonSatrom (英語) です。

この記事のレビューに協力してくれた技術スタッフの Paul RaynerClark Sell に心より感謝いたします。