Reuse Cucumber steps
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
Reusing cucumber steps in a large codebase/team
Write scenarios/steps that are about what you are doing and why you are doing it rather than about how you do things. Cucumber is a tool for doing BDD. The key word here is Behaviour, and its interpretation. The fundamental idea behind Cucumber and steps is that each piece of behaviour (the what) has a unique name and place in the application, and in the application context you can talk about that behaviour using that name without ambiguity.
So your examples should never be in steps because they are about HOW your do something. Good steps never talk about clicking or selecting. Instead they talk about the reason Why you are clicking or selecting.
When you follow this pattern you end up with fewer steps at a higher level of abstraction that are each focused on a particular topic.
This pattern is easy to implement, and moderately easy to maintain. The difficulty is that to write the scenarios you have to have a profound understanding of what you are doing and why its important so you can discover/uncover the language you need to express yourself distinctly, clearly and simply.
I'll give my standard example about login. I use this because we share an understanding of What login is and Why its important. Realise before you can login that you have to be registered and that is complex.
Scenario: Login
Given I am registered
When I login
Then I should be logged in
The implementation of this is interesting in that I delegate all work to helper methods
Given I am registered
@i = create_registered_user
end
When I login
login_as(user: @i)
end
Then I should be logged in
should_be_logged_in
end
Now your problem becomes one of managing helper methods. What you have is a global namespace with a large number of helper methods. This is now a code and naming problem and All you have to do is
- keep the number of helper methods as small as possible
- keep each helper method simple
- ensure there is no ambiguity between method names
- ensure there is no duplication
This is still a hard problem, but
- its not as hard as what you are dealing with
- getting to this point has a large number of additional benefits
- its now a code problem, lots of people have experience of managing code.
You can do all these things with
- naming discipline (all my methods above have login in their name)
- clever but controlled use of arguments
- frequent refactoring and code cleaning
The code of your helper methods will have
- the highest churn of all your application code
- the greatest need to be simple and clear
So currently your problem is not about Cucumber its about debt you have with your existing scenarios and their implementation. You have to pay of your debt if you want things to improve, good luck
How to reuse sessions or steps?
You have a couple of options to achieve this. One is to use the "Background" section in each feature file that replicates those exact steps. You could just introduce an extra validation on the step definition for example to check if Chrome is running then it would not be started again, etc. The caveats with this approach is that the login action still has to be performed on each feature and the background section needs to be included on each feature file.
Feature: Feature 1
Background: User is Logged In
Given Open Chrome and start application
When I enter valid username and password
Then User shall be logged in successful
Scenario: Scenario 1 in Feature 1
...
Scenario: Scenario 2 in Feature 1
...
Another option is to tag the scenarios that require the user to be logged in (e.g. @requiresUserLoggedIn) and check if that tag is present on a Before Hook, then trigger the login process if that is not the case. This is the approach I would follow.
@requiresUserLoggedIn
Scenario: Scenario 1 (requires user to be logged in)
...
Scenario: Scenario 2 (does NOT require user to be logged in)
...
@Before
public void setUp(Scenario scenario) {
if(scenario.getSourceTagNames().contains("requiresUserLoggedIn)) {
// Check if the User is Logged In and Trigger Login Process if that is not the case
}
}
Related Topics
Strange \N in Base64 Encoded String in Ruby
How to Solve "/Usr/Bin/Env: Ruby_Executable_Hooks: No Such File or Directory"
Difference Between Stdin and $Stdin in Ruby
Heroku - Cannot Run Git Push Heroku Master
How to Solve "Ruby Installation Is Missing Psych" Error
How to Override a Setter Method in Ruby on Rails
Error: Failed to Build Gem Native Extension on Mavericks
Office 365 Rest API - Daemon Week Authentication
Operator Precedence For And/&& in Ruby
Ruby 1.9 Array.To_S Behaves Differently
How to Avoid "Cannot Load Such File - Utils/Popen" from Homebrew on Osx
Rails: How to Change the Title of a Page
Protected and Private Methods in Rails
Does Ruby Have Real Multithreading
Is There a Reason That We Cannot Iterate on "Reverse Range" in Ruby