Showing posts with label end-to-end tests. Show all posts
Showing posts with label end-to-end tests. Show all posts

Sunday, March 6, 2016

Integrate Cucumber with Geb: Productive BDD Automation with Style

In this post, I show how Cucumber can be integrated with Geb for implementing end-to-end tests under the BDD paradigm, in a productive and stylish manner. As a system under testing, I choose the BBC website and test basic navigation on BBC news pages. The code is available on github.

Background

Geb is a Groovy framework for automation. It offers a powerful DSL on top of  WebDriver (aka Selenium 2). The benefit from using Geb instead of WebDriver directly is that Geb takes responsibility of the technicalities, removes the boilerplate and allows the developer to focus on the test framework he builds. It does that by exploiting Groovy meta-programming capabilities and Groovy's inherent conciseness. It has also built in support for test automation code design patterns, like the Page Object and the Loadable Component patterns, that have been proven critical for minimizing the code maintenance cost of end-to-end internal business test frameworks.

Cucumber is the most famous test framework to support the BDD paradigm. What BDD is and how much benefit agile teams can take by following it, has been discussed in one of my previous posts, about integrating cucumber with WebDriver. In this post, I follow the same objective, i.e. to automate a scenario where the user lands on the BBC home page, navigates to a news category (e.g. Business) and finally shares a video on facebook. In this post I effectively replace WebDriver with Geb and show the value of this replacement. The reader is highly recommended to read that previous post before continuing with this one, as well as comparing the two github repositories (bbc-webdriver-cucumber and bbc-geb-cucumber).

Setup

As a build and dependency management tool, I use Gradle. The build.gradle file follows. 

The Geb dependencies are geb-core and geb-junit4, as in this demo I use Junit4 (alternatives include TestNG and Spock). The Cucumber dependencies are cucumber-groovy and cucumber-junit. Also, in this demo I use the selenium-firefox-driver. Finally, the standard groovy-all dependency is added to be able to compile and run Groovy and junit to be able to use the Junit4 runner via the Cucumber runner class. Notice that some dependencies appear to be compile time dependencies, while some others testCompile. This is due to my habit when I develop test frameworks to store Cucumber Step Definition and Page Object classes under the src/ while the test runner class under the test/ directory. If someone selects to put them all under the test/ directory, then the testCompile status is enough for all the dependencies.

BBC News Feature

The demo scenario is similar to the one presented in the bbc-webdriver-cucumber demo, but is simplified and written in user terms.

Geb Page Objects

Due to its importance in writing effective and maintainable test frameworks, the Page Object pattern has built in support on Geb. In a sense, Geb forces the developer to follow this pattern, while WebDriver leaves it to his decision/knowledge. Let's look at NewsPage class to see how a Geb Page class looks like.


As one can observe the NewsPage class extends the geb.Page class and defines three static variables, url, at and content. The first one is of type String and defines the url of the web page modeled by the NewsPage class. The second one, i.e. at, is of type Closure and defines a boolean post-condition of the web page loading, i.e. a condition whose true value makes us sure the web page has been loaded correctly. This is normally a check to see if some representative web element is displayed. The content variable is of type Closure, which in turn contains a set of closures, each one representing a web element. For instance, the news closure represents the "NEWS" text in the upper left corner of the BBC news page that is written with white font on top of a red background. The news closure contains a locator for that web element. Geb uses a very powerful JQuery-style locator syntax. Although Geb allows you to work with the org.openqa.selenium.By static methods for defining locators, the gebbish JQuery-style syntax is much more flexible. When called, this closure returns a proxy of the locator. Similarly to WebDriver PageFactory functionality, Geb's locators are lazy, in the sense that they are resolved against the active web page the first time a method is called on them. For instance, when someone calls the at closure, the following steps take place:
  1. news closure is called 
  2. news closure returns a proxy of the web element located by the specified locator.
  3. the method isDisplayed() is called on that proxy
  4. the web element located by the specified locator is initialized (NoSuchElementException can be thrown if no element is found) 
  5. isDisplayed() returns true or false, depending on if the web element is currently displayed or not on the active web page
It might not be obvious to someone unfamiliar with Groovy, but news.displayed is equivalent to news().isDisplayed(). If a closure has no argument you can omit the parentheses. Also, the Java-style named getters getX() for a field x (or isX() if x is of boolean type) are equivalent with x in Groovy.

The second, i.e. categoryLink, closure defined inside the content closure is even more interesting. Since categoryLink is a closure, it can take parameters. This gives you the very powerful ability to define a group of locators, that differ only in the specified parameters. For instance, categoryLink can locate all the links in the BBC news page navigation bar. Each one of these links points to a different news category page, e.g. Business, Politics etc. All those links differ only on their text (and of course on the url they point to) and therefore can be defined in a unified manner.

Finally, NewsPage class defines a method for navigating to the NewsCategory page representing the specified category. This method returns the NewsCategorPage object following the "strong" page object pattern, that requires for any method to return the page object that will be active after its call. The call categoryLink(category) returns the proxy of the link located by the locator corresponding to the specified category and then the click() method is called on this element to navigate to the specified category web page. At this point we need to notify Geb that the active page object has been changed. This is done with the browser.page(new NewsCategoryPage(category: category)) call. A lot of things take place here, so let's examine this call in detail.

First a new NewsCategoryPage object is created and its category field is initialized with the specified category string. Then, this object is passed as argument to browser.page method. The browser object is a field of the superclass geb.Page. Its type is the Browser class, which is effectively a wrapper around a WebDriver object. By default Geb uses the FirefoxDriver, but this is configurable. The browser object exposes the driver object, e.g. you can access it by writing browser.driver (which is equivalent with browser.getDriver()) and work with this directly but in most cases you don't need to do this. You may notice that there is no return statement in the selectCategory method. This is a famous Groovy idiom, the method returns the object that the last expression is evaluated to. In this case, browser.page(new NewsCategoryPage(category: category)) returns the page object that is passed as argument, which is of NewsCategoryPage type.

Steps Implementation

The Step Definition file follows.

As one can easily observe, there is no class, step definitions are written in a script form. In Groovy, classes are optional, meaning that Groovy can be used as a scripting language. Before and After are cucumber Hooks that run before and after each scenario since I don't pass any parameter (they could run only before/after some scenarios specified by their parameters). 

The Before hook block deserves some explanation. With the BindingUpdater class, we inject a Browser object in this script. BindingUpdater is a Geb class which wraps around the Groovy Binding class. This is normally used in Groovy scripts to inject objects. Any programming language supports some concept of Binding (some of them call it "environment"), e.g. if you define a variable x = 5, then the language at runtime creates a variable and bounds it to the value "5". After running the Before hook, each scenario has access to the injected browser object, which has been bounded to an instance of Browser class. 

For instance, the first step implementation block is to HomePage. In Groovy, parentheses are optional, if a method has a single parameter, e.g. to HomePage is equivalent with to(HomePage). Second, in Groovy classes are first class citizens, e.g. to refer to HomePage class, you can write HomePage, instead of HomePage.class (the ".class" is optional). Since there is no method named to taking as argument the HomePage class, one could ask how this script runs successfuly. The trick is that such a to method is defined in the browser object that has been injected to the script. Therefore, to HomePage is equivalent with browser.to(HomePage.class). You could write it like that, but the Gebbish point is to get rid of the boilerplate and stick to the concise and stylish to HomePage syntax. The idea is you should focus on your test goal which at this point can be described as "go to BBC home web page". All the rest are technicalities that Geb handles for you.

If you read this script, you will realize I only used the to method to load the first web page, which is the BBC home page. For all the rest navigations, I followed the "strong" page object pattern with calling methods exposed by page objects to navigate to other web pages, like page.selectLatestNews() which navigates from the BBC home page to the BBC News page. Each time the page object (this is equivalent to browser.page) is the page object corresponding to the active web page. This is the result of me following the "strong" page object pattern. The page object is defined with type geb.Page in the Browser class, which does not define any selectLatestNews method, but Groovy supports a dynamic type system (aka duck typing) by default (you can switch to static typing by using annotations though), i.e. it resolves the object types at run time. As a developer, I know that at this point the page object is of type HomePage and therefore has a method selectLatestNews. In the following line, I check if the active web page is the news category page to assure that this web page has been loaded correctly. This will invoke the at closure defined in the NewsPage class. 

Notice the difference with the following step implementation that includes def newsCategoryPage = page.selectCategory(category). This call implements the navigation from the news page to the news category page for the category specified in the feature file. Since this step can navigate to different category pages an at call with parameter the NewsCategoryPage class is not enough. NewsCategoryPage class models all the news category pages, while I want to be sure that the active web page is the one corresponding to the category specified in the feature file. To this end, I need to assign the page object returned by selectCategory and pass this as the at method parameter. Browser class overloads the at method, so that you can either pass a Page class (or list of potential Page classes) or a Page class instance (or a list of potential Page class instances).

Furthermore, notice that the first step implementation don't need to make an at call, since the to method does an at call at the end to check if the target web page has been loaded correctly.

Finally, all step definition "methods" are actually not methods but calls to Cucumber methods that take as argument a string pattern and a closure. If the final argument of a method is a closure, then Groovy allows this to be written outside of the parentheses. For instance the first step, i.e. 
Given(~/^the user is landed at the home page$/) { -> to HomePage }
is equivalent to 
Given(~/^the user is landed at the home page$/, { -> to HomePage }).

Runner Class

The Groovy version of the Cucumber Runner class is very similar to the Java version:

Run this on eclipse with "Run As"->"JUnit Test" or by command line with gradle clean test.

Outro

To sum up, Geb boosts the productivity of the automation developer beyond any preceding level. To achieve this it allows you to focus on what really matters, the web elements of pages under test and the interaction between web pages as defined by your product requirements. Gebs manages all the technical details for you. Also Geb forces developers to follow design patterns like the Page Object and the Loadable Component patterns which are critical for the minimization of the code maintenance cost. Since it is heavily relied on the Groovy meta-programming features, it requires the developer has some level of maturity with Groovy. The JQuery locator syntax might look unfamiliar to back-end developers but is really easy to learn and Geb has excellent documentation (not only about the locators). It integrates seamlessly with Cucumber and together they make a perfect combination for developing end-to-end test frameworks that require web page automation and are developed by agile teams following the BDD paradigm.

Note 04/02/2017:

I have upgraded to Selenium 3. 
You will have to download the geckodriver, unzip it and set the path to it on src/test/resources/GebConfig.groovy.
The project was tested with: 
  • selenium version 3.0.1
  • geckodriver 0.14.0
  • firefox 51.0.1

Monday, November 16, 2015

Integrate Cucumber with WebDriver: BDD in automation

In this post, I show how Cucumber can be integrated with Selenium WebDriver for bringing BDD in end-to-end testing. As a system under testing, I choose the BBC website and test basic navigation on BBC news pages. The code is available on github.

Background

Make no mistake, cucumber's main purpose is not to be a testing tool, but to enforce collaboration between scrum teem members. The main objective is to ensure that all the team members have the same understanding of the requirements of the feature they have to develop and test. Business analysts that, together with the product owner, have the basic idea of the feature requirements, introduce it to the team and the team as a whole finalizes the requirements in the form of Cucumber Scenarios. These scenarios are written in Gherkin, a DSL very close to natural language. Scenarios can be created in different level of granularity, e.g. for unit, integration or end-to-end tests. Cucumber initiates the tests that are initially failing, as the feature has not yet been developed and then development starts. Scenarios do not only guide the testing, but also consist a live documentation of the feature. 

In this post, I focus on end-to-end tests (frequently called 'automation'). These are typically written using the Selenium WebDriver framework. As it is well known, end-to-end tests have a high maintenance cost, e.g. when the UI changes, tests might fail, as some web elements may no longer be present or may have been moved. The work you need to fix them highly depends on the presence or absence in/from your test code of design patterns such as the Page Object and the Loadable Component patterns. In this post, I show the benefit for using them.

Setup

As a build and dependency management tool, I use maven. The pom file follows.

The Hamcrest library is optional, you can integrate Cucumber with Selenium WebDriver without it, but it is nice to have it, as it provides an elegant way to write assertions. Also I use 'cucumber-java8', as this allows for steps to be defined in lamdas instead of methods and this reduces the boilerplate.

BBC News Feature

Let's say we are in the team that develops/maintains the BBC website and in particular the news part. The feature we need to develop is that the user should be able to navigate from the home page to the news page, then select a news category, e.g. 'Science', then select a (let's say the first for simplicity) video page from the 'Watch/Listen' section and then share this video page on Facebook. As the feature is implemented by the real BBC team, you can try it. The sequence of the links could be the following, but always keep in mind that the Cucumber scenarios should be written before the development: 
  1. BBC Home
  2. BBC News
  3. BBC News Science category
  4. BBC Science Video Page
  5. Facebook referral page
The Cucumber feature file follows.

The first two scenarios test that the user can navigate to the BBC News page by clicking either on the 'Latest news' link of the homepage or on the 'News' link on the navigation bar at the top of the home page.

The third scenario, which more precisely is a 'scenario outline' or a set of scenarios if you wish, tests the main feature which is to navigate from home page to news page, then to a news category page, then to the first video page of the category and finally to share it on facebook.

Gherkin is very easy to follow, shortly Feature, Scenario, Given, When, Then, And are cucumber reserved keywords, while the rest is English. Each sentence starting with Given, When, Then defines a step. For more info about Gherkin, steps and scenario outline, you can read the post BDD with Cucumber vs Spock. The main thing to keep is a cucumber scenario is the analog of a junit/testng test method, while a scenario outline is the analog of a set of calls to the same testng test method that takes an input whose value is provided by a testng data provider. The cucumber analog for the data provider is the Examples table. In short, category is the input, and the scenario outline tests that the feature works for any BBC news category appearing in the Examples table.

The Page Object Pattern

The main goal of the Page object pattern is to encapsulate details related to a page into a class object and expose only methods relevant with the functionality this page offers. Take for instance the news category page, the locator of the first video page link in the 'Watch/Listen' section is an internal detail that should be hidden from tests. The news category page object only needs to provide a 'clickFirstVideo' method and that's the only thing test code needs to be aware of. 

The benefit is that when the UI changes and the locator is no longer valid, you will have to fix it on a single place, on the related page object. Experience shows that if you don't use the page object pattern, the end-to-end code maintenance cost will explode sooner or later.

The Loadable Component Pattern

While the page object pattern hides the details behind functionality offered by the page, the Loadable Component pattern hides the details behind the navigation to the page object. To apply this, WebDriver offers an abstract LoadableComponent class which your page object class has to extend. It has two abstract methods 'isLoaded' and 'load' that the page object class has to implement and a concrete method 'get' that calls 'isLoaded' to check if the page is already loaded and if not it calls the 'load' method and then the 'isLoaded' to verify it is finally loaded. The idea is to pass to the constructor of a page object class a reference to the parent page object and in the 'load' method of the child class to call first the 'get' method on the parent object (to load the parent page) and then call some functionality method on the parent object that will load the child page.

The implementation of the NewsCategoryPage class that implements both the page object and the loadable component patterns follows.

Notice the implementation of the 'load' method. It first calls 'parent.get()' to load the news page and then calls the 'selectCategory' method on the news page object to load the news category page.

It should be stressed that the loadable component pattern does not necessarily tight a page object class with a single navigation path. Take for instance the News page, you have 2 ways to navigate to it from the BBC home page by clicking on either 'Latest news' link on the main section, or on the 'News' link on the navigation bar. The relevant part of the news page object class follows (you can find the whole class code on github).

Steps Implementation

The Steps implementation follows.

You can run the scenarios either on eclipse by right clicking on the BBCNewsTest.java -> Run As -> Junit test, or with maven: mvn clean test

Further Improvement

A nice observation for the loadable component pattern is that that if the page object class implements it and you call the 'get' method on it, you implicitly run assertions that verify the page has been 'correctly' loaded, where 'correctly' is defined in the 'isLoaded' method implementation. Despite its name that starts with 'is' this method does not return a boolean value. Instead, its return type is void, while it 'throws Error'. This typically contains all the assertions needed to verify it was loaded properly.

To understand this, recall how get method is implemented in the LoadableComponent class.

It should now be obvious that the second scenario is redundant. If the news page does not load when you click on 'News' link on the home page navigation bar, then the Step "When the user clicks 'News' on the navigation bar", which is shared in the second scenario and the scenatrio outline, will fail, due to the assertions in the NewsCategoryPage 'isLoaded' method. Therefore the second scenario can be safely omitted.