おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

Capter 19 Cucumber Detail

rspec本一人読書会の続き。cucumberのお勉強メモです。

19.1 Step Definitions

定義されていないStepを含むfeatureを実行すると、cucumberはStep Definifionのひな形を出力してくれる。

19.2 World

全てのシナリオはWorldオブジェクトのインスタンスの文脈で実行される。Worldオブジェクトはデフォルトでは単なるObjectクラスのインスタンス

Worldを使って、各step_definitionで使うヘルパーメソッドを定義するには下記のようWorldメソッドの引数にModuleを指定してあげる。

module MyHelper
  def some_helper
    # ...
  end
end

World(MyHelper)

どこで上記のような設定をしてもいいけど、 features/support/world.rb に書くのがおすすめらしい。

Worldの元となるクラスを別のものにすることも出来る。下記のようにWorldメソッドのブロックにインスタンスを入れてやると出来るみたい。

class MyWorld 
  def some_helper
    #...
  end
end

World do
  MyWorld.new
end

上記はcucumber-railsのようにcucumberのプラグインを使う時のテクニック。

19.3 Calling steps within step definitions

stepをDRYにする方法のうちの一つは、高いレベルの抽象化をしたstepを作ること。そうするとたとえば下記のような書き方になるはず。

When /I transfer (.*) from (.*) to (.*)/ do |amount, source, target|
  When "I select #{source} as the source account"
  When "I select #{target} as the target account"
  When "I set #{amount} as the amount"
  When "I click transfer"
end

でも下記のようにも書ける。

When /I transfer (.*) from (.*) to (.*)/ do |amount, source, target|
  steps %Q{
    When I select #{source} as the source account
    And I select #{target} as the target account
    And I set #{amount} as the amount
    And I click transfer
  }
end

どちらでも同じなので書きやすい方を選択するといい。

上記の二つの例は、step definitionsからstepを呼んでいる。これはDRY的にはいいけどstepがstep呼んでそこから別のstep呼んで・・・となるとデバッグしにくくなる。また、抽象性にばらつきのあるstep definitionsになりやすい。どうするかはバランスをみて決めたほうがいい。

19.4 Hooks

cucumberは下記の三つのメソッドでフックを実現している。

Before
scnenarioの前
After
scenarioの後
AfterStep
stepの後

hookはfeatures配下ならどこでも定義できるけどfeatures/support/hooks.rbに書くのがおすすめらしい。

また、hookは何回でも定義できる。例えばBeforeが10個あっても問題ない。

tagged hooks

特定のscenarioだけで実行させたいhookがあるときに使う。

Before("@foo") do
  puts "This will run before each scenario tagged with @foo"
end

コマンドラインでの--tagsのようにするともっと複雑な条件を使える

Before("@foo,~@bar", "@zap") do
  puts "This will run before each scenario tagged with @foo or not @bar AND @zap"
end

visibility

hookは便利だけど技術者以外にはよく分からないという欠点がある。技術者以外の人にわかるようにしたい場合はBackgroundを使う。

19.5 Background

Backgroundは与えられたfeature内の全てのシナリオの前に実行される。featureのなかで参照したいときにBefore hookの代わりとして使う。Feature内で共通したGivenをくくってBackgroundにだしてやると見通しが良くなりそう。

BeforeはBackgroundより前に実行される。

19.6 Multi-line Text

下記のようにすると複数行のテキストを引数にとれる。marginは最初の"で決まる。

Scenario: pending implementation
  Given a file named "example_without_block_spec.rb" with:
    """
    describe "an example" do
      it "has not yet been implemented" end
    """
  When I run "spec example_without_block_spec.rb"
  Then the exit code should be 0
  And the stdout should include
    """
    Pending:

    an example has not yet been implemented \(Not Yet Implemented\)
    .\/example_without_block_spec.rb:2

    Finished in ([\d\.]*) seconds

    1 example, 0 failures, 1 pending
    """
  # ...

複数行のテキストは正規表現でキャプチャする必要はない。正規表現に含まれなかった文字列はブロックの最後の引数にはいる。

Given /a file named "([^\"]*)" with:/ do |filename, text|
  # ...
end

Then /the stdout should include/ do |text|
  # ...
end

step中の文字は正規表現で評価されるので、複数行のテキスト中に()等の、正規表現で使われる特別な文字がある場合はがある場合はescapeをしておく必要がある

19.7 Tables in Steps

wikiスタイルのフォーマットでtableを表現できる。cucumberは行の最初に "|" を見つけたときにパースして、Cucumber::Ast::Tableオブジェクトにする。Cucumber::Ast::Tableオブジェクトは、hashの配列を返すhashesメソッドを持っている。

CucumberはCucumber::Ast::TableをStep definition中の最後のブロック引数として渡す。

Scenario Outlines

Scenariosキーワードで、Scenario Outlineに流し込むデータをテーブルで定義できる。

Scenario Outline:
  submit guess Given the secret code is "<code>"
  When I guess "<guess>"
  Then the mark should be "<mark>"
Scenarios: all numbers correct
  | code | guess | mark |
  | 1234 | 1234  | ++++ |
  | 1234 | 1243  | ++-- |
  | 1234 | 1423  | +--- |
  | 1234 | 4321  | ---- |

ScenariosのエイリアスとしてExamplesキーワードもある。

複数行のテキストや、テーブルの値にもscenario outlineは使える。

Scenario Outline:
  Given a discount of <discount> When I order the following book:
    | title	| price	|
    | Healthy eating for programmers | <price> |
  Then the statement should read:
    """
    Statement for David
    Total due:	<total>
    """
Scenarios:
  | discount | price | total |
  | 10%	| $29.99 | $26.99 |
  | 15%	| $29.99 | $25.49 |

19.9 Configuration

cucumberのオプションをあらかじめ書いておくためのファイルとして cucumber.yml または config/cucumber.yml が定義されている。

例えば cucumber.yml に下記のように書いて

wip: --tags @wip features

下記のように実行すると

cucumber -p wip

下記のようにしたのと同じ結果になる

cucumber --tags @wip features

rails だと、自動的にdefaultと言う名前のprofileが探されるようなので

default: --format pretty

などとしておくと大変都合がいい。