Cucumber-JVMのHelloWorld
以前のブログでCucumber-JVMについて触れました。Cucumber-JVMは面白いプロダクトだし、使い方が非常に簡単なので、今回はCucumber-JVMのHelloWorldをブログに書きます。Cucumber-JVMの概要については以前のブログをご参照下さい。では、HelloWorldの手順を書いていきます。
Mavenでライブラリを取得
pom.xmlに依存ライブラリを追加します。
<dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-picocontainer</artifactId> <version>1.1.1</version> <scope>test</scope> </dependency> <dependency> <groupId>info.cukes</groupId> <artifactId>cucumber-junit</artifactId> <version>1.1.1</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency>
シナリオを記述
Gherkin(ピクルス用の小さなキュウリを意味する)と呼ばれるフォーマットでシナリオを書きます。ファイル名の拡張子は、「.feature」にし、任意のパッケージに格納します。以下にシナリオのサンプルを示します。
Feature: 車の燃料の補給 運転手は、車を走らせるために、燃料を補給する必要がある。 Scenario: 燃料の補給 Given 車のタンクには、10リットルの燃料が入っている When 運転手は、50リットルの燃料を補給した Then タンクには、60リットルの燃料が入っている
「Senario:」がシナリオの始まりの部分です。その下にステップを記述します。
ステップの先頭のキーワードの種類には、Given、When、Then、And、Butがありますが、Cucumber-JVMにとって違いは関係ないので(ステップの順番にテストコードを実行するだけ)、使い分けはシナリオを記述する人の裁量で自由に行います。
また、1つの「.feature」ファイルに、複数のシナリオ(「Senario:」)を記述することができます。ファイルは任意のパッケージに配置します。ここでは、CarMaintenance.featureというファイル名でcom.genba.carパッケージに配置することにします。このHelloWorldでは、最終的には以下のようなファイル構成になります。
実行のためのクラスを作成
テストコードを記述クラスは後ほど紹介しますが、まずは、テストを実行するためだけのクラスを作成します。フィールドもメソッドも持たず、Annotationの@RunWithでCucumber.classを指定するだけです。このクラスを実行すると、パッケージ配下(サブパッケージ含む)のシナリオ(「.feature」ファイル)とテストコードが紐付けられてテストが実行されます(明示的に「.feature」ファイルとテストコードを指定することもできます)。以下にサンプルを示します。
package com.genba.car; import org.junit.runner.RunWith; import cucumber.api.junit.Cucumber; @RunWith(Cucumber.class) public class CarTest {}
実行のためのクラス(CarTestクラス)をJUnitで実行
まだテストコードを記述してませんが、ひとまずこの状態でCarTestクラスをJUnitで実行します。すると、コンソール画面に以下のメッセージが表示されます。
You can implement missing steps with the snippets below: @Given("^車のタンクには、(\\d+)リットルの燃料が入っている$") public void 車のタンクには_リットルの燃料が入っている(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @When("^運転手は、(\\d+)リットルの燃料を補給した$") public void 運転手は_リットルの燃料を補給した(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @Then("^タンクには、(\\d+)リットルの燃料が入っている$") public void タンクには_リットルの燃料が入っている(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); }
シナリオのステップに紐づかなかったテストコードの雛形が出力されています。
テストコードの雛形の作成
Cucumber-JVMでは、シナリオのステップごとにテストメソッドを作成します。先ほどコンソール画面で表示されたテストコードは、3つのテストメソッドが含まれており、それぞれ、シナリオのステップに該当しています。このテストメソッドのことをStep Definitionと呼び、Step Definitionを定義しているテストクラス群をひとまとめにしてStep Definitions(複数形のsが付く)と呼びます。まずは、この雛形をコピペしただけのテストクラスを、実行のためのクラス(CarTestクラス)のパッケージ配下(サブパッケージ含む)に作成します。ここではCarMaintenanceStepsクラスにします。以下にサンプルを示します。
package com.genba.car; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import cucumber.runtime.PendingException; public class CarMaintenanceSteps { @Given("^車のタンクには、(\\d+)リットルの燃料が入っている$") public void 車のタンクには_リットルの燃料が入っている(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @When("^運転手は、(\\d+)リットルの燃料を補給した$") public void 運転手は_リットルの燃料を補給した(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } @Then("^タンクには、(\\d+)リットルの燃料が入っている$") public void タンクには_リットルの燃料が入っている(int arg1) throws Throwable { // Express the Regexp above with the code you wish you had throw new PendingException(); } }
Annotationの中の文字列は正規表現になっています。Cucumber-JVMは、ステップとStep Definitionの紐付けを正規表現のマッチで行います。アノテーションの種類も関係ありません(例えば、ステップがWhenでStep Definitionが@Givenでも、正規表現がマッチすれば紐付けられます)。ちなみに、1つのシナリオが複数のテストクラスのStep Definitionを利用することもできるし、複数のシナリオが同じStep Definitionを利用することもできます。また、1つのステップが複数のStep Definitionにマッチしてしまうとエラーになります。「(\\d+)」のところはステップで可変で設定できる数字を表し、ステップで指定した値がテストメソッドの引数に設定されます。数字のほかにも、文字列や日付なども可変にすることができます(可変の値の種類についてはこちらを参照)。
この状態で、実行のためのクラス(CarTestクラス)を実行すると、PendingExceptionが発生し、コンソール画面で未実装のStep Definitionが出力されます。以下にメッセージを示します。
cucumber.runtime.PendingException: TODO: implement me at com.genba.car.CarMaintenanceSteps.車のタンクには_リットルの燃料が入っている(CarMaintenanceSteps.java:12) at ?.Given 車のタンクには、10リットルの燃料が入っている(com\genba\car\CarMaintenance.feature:5)
Step Definitionsの作成
Step Definitionsの中身を記述します。以下にサンプルを示します。テスト対象のCarクラスも合わせて示します。
package com.genba.car; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertThat; public class CarMaintenanceSteps { private Car car; @Given("^車のタンクには、(\\d+)リットルの燃料が入っている$") public void 車のタンクには_リットルの燃料が入っている(int arg1) throws Throwable { car = new Car(arg1); } @When("^運転手は、(\\d+)リットルの燃料を補給した$") public void 運転手は_リットルの燃料を補給した(int arg1) throws Throwable { car.addFuel(arg1); } @Then("^タンクには、(\\d+)リットルの燃料が入っている$") public void タンクには_リットルの燃料が入っている(int arg1) throws Throwable { int actualFuelLevel = car.getFuelLevel(); assertThat(actualFuelLevel, is(arg1)); } }
package com.genba.car; public class Car { private Integer fuelLevel; public Car(int initialFuelLevel) { fuelLevel = initialFuelLevel; } public void addFuel(int addedFuel) { fuelLevel = fuelLevel + addedFuel; } public int getFuelLevel() { return fuelLevel; } }