6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Turnipで他のシナリオを実行するステップ

Last updated at Posted at 2017-11-17

状況

TurnipでGivenScenarioしたい!

Turnip向けに書いたGherkinシナリオが、他のシナリオに書いたステップで実現される状況を前提としている。
このため、前に書いたシナリオAを今書こうとしているシナリオBにおいて「前提」ステップとして再利用したい。

こんな風に書けたらいいな

機能: Hoge
  背景:
    前提 Fugaする

  シナリオ: Aを登録
    もし Piyoする
        ならば PiyoPiyo

  シナリオ: Bを登録
    前提 「Aを登録」しておく
    もし Hogeraする
        ならば HogeraHogeHoge

Cucumberの各ホスト言語における実装は知らんけど、Turnipの場合割とシンプルに内部でRSpecのdescribe ... context ... it構造にマップされているため頑張ればなんとかなる。

このfeatureに対応して内部で生成されているのはだいたいこういうコードだ。

feature = Turnip::Builder.build(feature_file)

describe "機能 Hoge" do
  background_steps = feature.backgrounds.map(&:steps).flatten
  before {
    background_steps.each do |step|
      run_step(feature_file, step)
    end
  }
  
  feature.scenarios.each do |scenario|
    describe scenario.name do
      # "Bを登録 前提 「Aを登録」しておく -> もし Hogeraする -> ならば HogeraHogeHoge"みたいなやつ
      description = ()
 
      it description do
        scenario.steps.each do |step|
          run_step(filename, step)
        end
      end
    end
  end
end

我々がstepの定義を書くときはこのrun_stepから呼び出された中にいる。従って、現在のitブロックへのアクセスだけできればscenarioにもfeatureにもアクセスできる。
これを利用すると次のように書ける。

step '「:name」しておく' do |name|
  context = RSpec.current_example.metadata[:block].binding
  current_feature = context.local_variable_get(:feature)
  filename = context.local_variable_get(:filename)

  scenario = current_feature.scenarios.find {|scenario| scenario.name == name }
  scenario.steps.each do |step|
    run_step(filename, step)
  end
end

今後の課題

残念ながら、このままでは他のfeatureに属するシナリオにアクセスすることはできない。
ローカル変数featureはそのfeatureのためのdescribeを構築しているブロック内にしか存在しないので、そのままでは他のfeatureを現在のit内から参照する術はないのである。

ただし、RSpecがdescribeをネストしたRSpec::Core::ExampleGroupのサブクラスにマッピングする内部APIはもう何年も安定していて、そこそこ安心して利用できそうだ。したがって、ObjectSpace.each_objectでそれっぽいExampleGroupインスタンスを浚って目標のfeatureに相当するものを特定すれば、なんとかする余地はありそうだ。

警告!

Cucumberにおける公式の見解は「そういう機能はサポートしない」らしい。代わりに、Cucumberのホスト言語で高度なヘルパーメソッドを書いてそれをステップ定義で利用したり、前提状況を再現するWorldを作ったりするのが推奨されている。

本稿はそれでもなんとかしたいという話であるが、以下に見るように(少なくともcucumberでは)あまり歓迎されていない手法である。

I initially thought this was a great feature...
However... in the longer term it produces scenarios that are hard to maintain and understand

this is a common request that we are not going to implement

We implemented this feature (we call it "nested steps") in the Ruby Cucumber, and it's been hard to maintain, and led to some very complex dependencies in people's test code.

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?