This short article was written in order to share that there are patterns and tools that, when combined, can provide automated tests with high business value and low cost in terms of code maintenance.
Cucumber is a tool that supports behaviour driven development (BDD), which is describing the behaviour of a user. This way the actual requirements of the user are described. Selenium WebDriver is a tool that simulates user actions on web browsers. This article describes how to use Cucumber along with Selenium WebDriver to implement automated tests with high business value and low maintenance.
Cucumber is describing business value in a natural language, so it allows software development teams to describe how software should behave in plain text, writing specifications by examples. A good advantage of writing specification with Cucumber is that anyone on the team can read the specifications in plain text - from business people to software developers. Besides, it helps to get feedback from the business stakeholders about whether the team is going to build the right thing before they get started. It also helps the team to make an intentional effort to develop a shared, ubiquitous language for talking about the system. Another advantage is that the specification is a living documentation, because even though it is written in plain text, it becomes executable automated tests as you will see later on in the article. The structuring approach that Cucumber uses for the specifications is the Given/When/Then format in compliance with the Gherkin language grammar. The Given part describes the existing pre-condition of the software state before you begin the behavior you are specifying. The When section is the behavior itself. The Then part describes the expected result of the behavior. For instance: Given I sign up for Book Store when I login with my credentials then I see a message "Welcome to Book Store!". Keep reading to find more examples.
It is possible to write automated tests with Cucumber in different natural languages such as Portuguese and English, among over 40 other languages. On the other hand, Cucumber does not directly interact with the software application. That is why it is usually used together with tools like Selenium WebDriver. This way, the automated tests serve as documentation because it can be written in a business-readable domain-specific language. It can also be executable because it has the Selenium WebDriver layer running behind the Cucumber layer; and it can be easily maintainable because the Cucumber layer doesn't need to know how the user actions are simulated, as this is Selenium WebDriver's job. Therefore, if the Web Application changes the way the user interacts with the application, all we need to do is change the Selenium WebDriver layer and keep the Cucumber layer as is. Read on to see examples of Cucumber and Selenium WebDriver layers.
There are other tool options for test cases management such as TestLink, which stores test cases written in natural languages. These tools tend to be disconnected from the actual code and become out of sync quickly. The greatest advantage of Cucumber over TestLink is that the tests written in plain text are also automated tests, which will always fail when the application goes out of sync with the Cucumber scripts. This helps to keep documentation up to date because failing will notify us if it is not. Cucumber also supports test case execution since it is easier to identify which feature scenarios are running by reading natural language instead of reading programming language.
Following are 3 steps to write automated tests with high business value and low maintenance: define business value, automate tests and refactor for low maintenance.
Following are some BDD principles used in Cucumber in order to write automated tests with high business value.
It is helpful to write them in plain text before it is implemented and make sure the business stakeholders give feedback about whether the tests describe the right software behavior. If the business stakeholders give the feedback after the software behavior is already implemented, it will probably take a lot longer to fix the code than it would take to fix a plain text test description. So automated tests written before building the software brings value by saving a lot of software development time.
Writing narratives also brings value to the test because it describes in one sentence why to implement the feature in the first place, and helps to understand what the scenarios from a feature are all about. For instance: In order to identify dinosaurs, as a bones keeper I want to access information about dinosaurs.
Another important quality in valuable automated tests is that the plain text uses a vocabulary specific to the business domain so that they are understood by anyone in the team: user, business stakeholders and software development team. This brings value for team communication because it makes the team develop a shared, ubiquitous language to talk about the software and focus on the business instead of describing tests using technical terms that are just another variant of a programming language.
In order to have automated tests with high business value, Cucumber consists of a business-facing and a technology-facing side.
Source: Image from the book: The Cucumber Book: Behaviour-Driven
Development for Testers and Developers, by Matt Wynne and Aslak Hellesøy
The business-facing side is defined within a feature file with .feature extension. It contains the feature description and the scenarios with the steps written in natural language, such as English. For instance:
Source: Feature file translated to English from https://github.com/taisedias/selenium-cucumber
The business-facing side by itself is not an executable automated test which exercises the feature of the application (that can be a Web, Android or WebService application). In order to make it executable, Cucumber has a technology-facing side that consists in step definitions, support code and automation library. The step definition is implemented using a programming language for each scenario step in the feature files. That code uses a test automation library such as Selenium or Watir (another example of WebDriver build with Ruby), in order to access the application and execute the automated tests.
Source: Translation of diagram in https://www.slideshare.net/taisedias/cucumber-qanight
The following example shows the technology-facing side implemented in Java. Each Java method is a step definition described in the feature file from the previous example. The method body uses the support code which calls Selenium automation library that contains the WebDriver that will finally access the application:
A pattern widely used in automated tests implementation is the PageObject pattern, which basically consists of mappings between the application page elements and a class. It also defines the user actions on that page by using its elements. The following example is a PageObject LoginPage, which maps the login page elements (such as username field, password field and button), as well as defines the actions such as log into the page. The actions drive Selenium to access and action the elements from the LoginPage.
The following example is a PageObject representing the login page. This object maps its individual elements with their identifiers for the driver to use. This should be the single place where the identifier is recorded.
Test code is code, so it is as important to refactor test automation code as it is to refactor application code. Otherwise the tests are going to be so hard to maintain that it will be better to throw everything away and start everything all over again. This is a cost that can be avoided by following some tips which help to reduce maintenance costs of the Java code, feature files and step definitions.
The usage of PageObject pattern makes it easier to maintain automated tests, because changes to page elements are only changed once in the PageObject. Feature files and step definitions do not hold specific information about the page and hence do not need to be updated. In other words, if any element in the Login page changes (such as URL path or button name), the maintenance will be done in the LoginPage.java only, while there will be no need to update the feature file (which contains the scenarios) and the LoginStepDefinition.java class.
It is important to write declarative features, so that scenarios are written like a user would describe them, which are highly maintainable compared with scenarios that only describe clicking links and filling in form fields. For instance:
Another tip to reduce maintenance cost in Cucumber scenarios, is to avoid steps that contain two actions, because one of them might be reusable in different scenarios:
Some other good practices that helps to maintain Cucumber tests can be found here http://blog.codeship.com/cucumber-best-practices/.
In short, this article briefly describes the use of Cucumber with Selenium WebDriver to implement automated tests with high business value and low maintenance using the BDD and PageObject patterns. All the presented code is on github. Even though we talked about Cucumber and Selenium, there are also other similar BDD tools and Web Drivers that can be combined to build an automation suite with high business value and low maintenance, as long as you use the practices suggested in this article.
Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Thoughtworks.