重复使用黄瓜步骤

我想重复使用一些黄瓜步骤,但似乎找不到正确的方法。

我想写下这样一个步骤:

Given /^I login with (.*) credentials$/ |type|
# do stuff with type being one of "invalid" or "valid"
end

然后再来一步:

Given /^I login successfully$
# call "Given I login with valid credentials"
end

因此,在测试用户身份验证时,我可以使用前者,但在大多数其他地方,我可以使用后者,而不必实际复制代码。

有没有办法调用另一个步骤,或者我只是把逻辑放在一个助手方法中,然后从每个任务中调用 said 方法(基本上就是一个方法提取重构,在阅读了我的问题之后,让我相信这实际上是最好的方法) ?

47930 次浏览

UPDATE: The method described below has been deprecated. The recommended way to call a step from within another step now looks like this:

Given /^I login successfully$/
step "I login with valid credentials"
end

Old, deprecated method (for reference):

You can call steps from other steps like this:

Given /^I login successfully$/
Given "I login with valid credentials"
Then "I should be logged in"
end

If all of the scenarios within a feature require this (or other steps), you can also add a Background to each features, with the common steps, like so:

Background:
Given I log in with valid credentials


Scenario: Change my password
Given I am on the account page

Note that the method for calling steps within steps has changed in recent versions of cucumber, which you'll see if you get an error like "WARNING: Using 'Given/When/Then' in step definitions is deprecated, use 'step' to call other steps instead:/path/to/step_definitions/foo_steps.rb:631:in `block in ' ". See the cucumber wiki for details.

The gist of the change is that you should now use the step or steps methods.

When /^I make all my stuff shiny$/
step "I polish my first thing"
end


When /^I make all my stuff shiny$/
steps %Q{
When I polish my first thing
When I shine my second thing
}
end

Best wrap your steps in %{} rather than quotes. Then, you don't need to escape double quotes which you'll need to use frequently.:

Given /^I login successfully$
step %{I login with valid credentials}
end


Given /^I login with (.*) credentials$/ |type|
# do stuff with type being one of "invalid" or "valid"
end

Calling steps from step definitions is a bad practice and has some disadvantages:

  1. If scenario will fail and there are nested step invocations, you will get only the last invoked step definition in the stack trace. It may be hard to find from which place that last stepdef was called
  2. Call to stepdef is sometimes harder to find and read than ruby method
  3. Ruby methods give you more power than calling steps from step defs

Aslak Hellesøy recommends to extract popular actions to World instead of reusing steps. It isolates those actions in one place, makes this code easier to find. You can extract code to usual Ruby classes or modules as well.

#/support/world_extensions.rb
module KnowsUser
def login
visit('/login')
fill_in('User name', with: user.name)
fill_in('Password', with: user.password)
click_button('Log in')
end


def user
@user ||= User.create!(:name => 'Aslak', :password => 'xyz')
end
end
World(KnowsUser)


#/step_definitions/authentication_steps.rb
When /^I login$/ do
login
end


Given /^a logged in user$/ do
login
end

Here is a useful discussion on the subject in Cucumber mailing list - link

Reuse keywords in feature file which will provide code reusability.

It is highly NOT recommended to call step defs within step defs.

I would write my feature file this way,

Scenario Outline: To check login functionality
Given I login with "<username>" and "<password>"
Then I "<may or may not>" login successfully


Examples:
|username|password|may or may not|
|paul    |123$    |may           |
|dave    |1111    |may not       |

In my step definition, (This is Java)

@Given(I login with \"([^\"]*)\" and \"([^\"]*)\"$)
public void I_login_with_and(String username, String password){


//login with username and password


}


@Then(I \"([^\"]*)\" login successfully$)
public void I_login_successully_if(String validity){


if(validity.equals("may")){
//assert for valid login
}
else
if(validity.equals("may not")){
//assert for invalid login
}
}

In this way, there is a lot of code reusability. Your same Given and Then handles both valid and invalid scenarios. At the same time, your feature file makes sense to the readers.