Reuse Cucumber Steps

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



Leave a reply



Submit