0

I am doing BDD test on an app with cucumber, and I want to have clear instruction as it is recommanded in cucumber doc. The thing is that we have to do reusable step definitions so the maintenance cost is acceptable. Example of scenario we have

Given I am on project page
When I click on 'buttonAddProject'     //not easily readable
And I click on 'switchProjectPrivate'
And I click on 'buttonDeleteProject'
etc..

I don't want to have a function for each step like that: I change projet visibily or I delete project, because this is basically just a click on a button, and we are going to have hundred of function like this. I also can't change the param in key to something more suitable, because every button key should be unique to avoid ambiguity.

So is there a way to do this with cucumber ?:

Given I am on project page
When I click on 'Add'      //easily readable
And I click on 'Private'
And I click on 'Delete'

Bindings: //this keyword doesn't exist
'Add' : 'buttonAddProject'  
'Private': 'switchProjectPrivate'
'Delete':'buttonDeleteProject'

I have tried that:

Scenario Outline:
Given I am on project page
When I click on <Add>    //easily readable
And I click on <Private>
And I click on <Delete>

Examples:
|Add               |Private               |Delete              |
|'buttonAddProject'|switchProjectPrivate'|'buttonDeleteProject'|

it works... but I need to do this for every scenario in the file, and if I really want to use scenario outline to iterate several times, I would have to copy paste this for every line, not really what I want.

How to organize this tests to make them more readable without making things to complex ?

Florent Bouisset
  • 962
  • 1
  • 5
  • 11
  • This is something that works for specflow, possibly works for cucumber too: https://stackoverflow.com/questions/11418074/how-to-increase-reusability-between-specflow-gherkin-steps. – Mate Mrše Sep 17 '20 at 12:37

2 Answers2

1

First of all Cucumber scenarios that show HOW each thing is done are not maintainable or particularly useful.

What are cucumber scenario should describe and document is WHAT you are doing. To do this you need to determine WHY you are clicking on these buttons and what is achieved by these actions.

Now I have no idea from your scenarios about WHAT you are adding, WHY it is private or WHY you are then deleting it. But I can speculate from your post. The scenarios you should be writing should be something like.

Scenario: Delete a project
  Given there is an existing project
  And I am viewing the project
  When I delete the project
  Then ...

Scenario: Create a project
  When I create a project
  Then a project should be created

When you write your scenarios in this manner you push the details of how you interact with your UI down into your step definitions. So you might have something like

When 'I create a project' do
  visit project_page
  click "Create Project"
end

or better just

When 'I create a project' do
  # must be on project page
  click "Create Project"

When you work this way step definition re-use becomes less relevant and valuable. Each step does more and does something more specific.

You can continue this pattern of pushing the HOW down by having step definitions make calls to helper methods. This is particularly useful when dealing with Given's which get alot of re-use. Lets explore this with Given there is an existing project

Given 'there is an existing project' do
  @project = create_project
end

Here we are pushing how we create an existing project down into the helper method create_project. The crude way to this would be to go through your UI visiting the project page and adding a new project. However this is really slow. You can optimise this process by bypassing your UI.

The most important point, whatever you decide to do, is that you are taking HOW you do something out of Cucumber and into some underlying code so now Cucumber is only interested in WHAT you are doing and WHY its important.

Making this change is probably the single most important thing you can do when Cuking. If you keep the HOW in your cucumber scenarios and step definitions you will end with a large number of brittle step definitions and very large scenarios that break all the time because everything is coupled together. You will get lots of bugs where making a change to get one step definition working causes lots of other scenarios to break. You will get lots of bugs where small changes to how you do a particular thing cause lots of unrelated scenarios to break.

Finally you are not doing BDD if you are writing the test after the code has been written. You can only do BDD if you write your scenarios collaboratively before the code is written.

diabolist
  • 3,990
  • 1
  • 11
  • 15
  • I guess you are right, I should have higher level step definitions such as `Given 'there is an existing project'` , that may not be as reusable as step definitions we have now, but I will make things cleaner in the scenarios. Because we have a lot of little actions such as click, type, etc.. some scenarios are barely understandable, or need at least some time before getting the point of the scenario. Our cucumber test almost look like function call: I click 'there', I should have snackbar with text 'Added' etc... Yes the code is already written and we are doing the scenario after. – Florent Bouisset Sep 18 '20 at 10:32
0

Each step must be tied to a step definition. If you like to reuse an existing step def, you can just pass the command as argument (" Add", "Private","Delete"). You will have to use both the scenario name and the corresponding command to perform the required action.It will be something like this,

Scenario: scenario1_deleteproject
Given I am on project page
When I click on 'Add'      
And I click on 'Private'
And I click on 'Delete'

Scenario: scenario2_createproject
Given I am on project page
When I click on 'Add'      
And I click on 'Private'
And I click on 'Delete'

The step definition:

@When("When I click on {string}")
public void I_Click_On_Something(String command)
{
Switch(Command)
{
case Add:
//perform steps here
case delete:
//perform steps here
default:

}

If you want to differentiate the commands between the scenario, you will have to use scenario name ( need a class with definitions of scenario & command). You can grab the scenario name @Before hook.

Sureshmani Kalirajan
  • 1,938
  • 2
  • 9
  • 18