I am trying out cucumber for browser testing and am learning about BDD testing for the first time – so i am quite a beginner to BDD. I have encountered a problem and would like to know the best-practice way of solving it.
(I am using it for a node.js webapp, so it is cucumber.js and selenium WebDriverJS, but the platform should not matter for this question)
DESCRIPTION:
In tutorials for the Gherkin syntax, you often see examples on clicking a button at a specific page, like for example:
Given (something)
When I click the submit button
Then (something)
Implementing the step-definition for this step is quite simple – just make selenium locate the element with whatever css-selector happens to match the button and then make selenium click on it.
PROBLEM:
But what if you have different buttons on different pages with the same "human readable" name (i.e. the same name in the cucumber step-text), but which must be located by different css-selectors?
It seems that you can't have step-definitions which are local to features, but that all step-definitions are shared among all features. This means that if you create a step "I click the submit button", as above, the step-definition must work for testing all pages in the entire webapp which has a submit button. I am not really sure what is the proper way to do this.
QUESTION:
What is the best practice for handling this?
EXAMPLE OF THE PROBLEM:
Let's say we have 3 pages which all have a ”next” button, which does something completely different on each page and are located completely differently in the DOM. Let's say we have one feature for each of these pages. In each feature, the scenarios involving the ”next” button looks like this:
Given I am on page xyz
And ...
And I click the next button
And ...
When ...
Then ...
The problem is that on the first page, the ”next” button is perhaps located by ”.next-button”, on the second page it could be ”#someContainer .btn.btn-primary” and on the third page”#assetButtons li:nth-child(3)”. If we had a step-definition local to each feature, they could simply look like:
this.Given(/^I click the next button$/, function(callback) {
this.driver.findElement(this.webdriver.By.css(”.next-button”).click();
callback();
});
this.Given(/^I click the next button$/, function(callback) {
this.driver.findElement(this.webdriver.By.css(”#someContainer .btn.btn-primary”).click();
callback();
});
this.Given(/^I click the next button$/, function(callback) {
this.driver.findElement(this.webdriver.By.css(”#assetButtons li:nth-child(3)”).click();
callback();
});
But since the step-definitions are global to all features, and you naturally can't create two step-definitions for the same regex, the step-definition for "I click the next button" need to know which page we are on or which ”next” button the scenario is referring to.
Some of my own thoughts: optional reading
- Could make the step-names less generic and make them also refer to which page the button is on: "I click the next button on the front page". The problem with this is that we already wrote in the scenario that we are on that page, so it is redundant.
- Could make up different names for the ”next” button on the different pages – like ”front page next” and ”search page next”.
- Could make the step-definition detect which page you are currently on and use different css-selectors depending on the page. (It could query selenium for the current URL, for example, and lookup which css-selector to use, based on both the button-name from the step and the current page).
- Could include the css-selector in the step-text itself. But that is not quite human readable and doesn't seem like the BDD way.
- Choose a higher abstraction level, where you don't talk about buttons, etc, but the higher level action, such as "I go to the next page in the search results". (does not seem like a good approach in general – often the exact steps are important, since the same action can often be done in different ways, and all of those paths should of course be exercised by the test. For example, if you could also go to the next page by pressing "N" or swiping, this would only test 1 of these cases (but could of course include that in the step-text too).)
But what is the RIGHT way to do it?