For the last two JIRA releases we have been working hard to revamp our in-browser acceptance test suite by developing a library of reusable page objects. In this three-part blog series I will talk about what we did and how JIRA plugin developers can make use of that work.

Browser automation – the Beauty, or the Beast?

It’s nice to have a suite of lightning fast unit tests with high coverage of your production code, but in a large and complex product like JIRA you can’t really do without functional tests that verify end-to-end behaviour of the application. As we add more and more sexy front-end features that rely on JavaScript/AJAX/REST stack, the value of such tests, in particular the ones running in a real browser, can only ever increase. Unfortunately, with high value comes high price.

Early attempts

Our first attempt at developing an automated-browser test suite using Selenium was not really a huge success. At some point the number of non-deterministic failures in our Selenium suite was so high that we started loosing discipline in chasing down and fixing the failures, whether intermittent or caused by actual bugs. This was on one hand certainly due to deficiencies in Selenium – when it works, it’s a great tool, but trying to debug any problem can turn into a real nightmare! On the other hand we really could have done a better job at designing the suite. Instead it sprang up as a bunch of lengthy ‘macros’ calling low-level Selenium API line-by-line. This resulted in lots of duplication, the number one maintainability problem in acceptance tests. Every time we changed the ID of an element, or just managed to work around a problem in Selenium, the fix had to be replicated among all the affected tests.

Problems to solve

So we really had two problems: the one of technology, but most of all the one of design. With good design in place you can easily introduce work-arounds and mitigate the impact of the underlying framework on the stability of your test suite. Without good design even the best technology will fail.

Page objects – object oriented functional tests

Enter the world of page objects. Essentially, page objects are just an application of very established object oriented practices in the world of automated browser tests. When testing a web application, many tests must interact with the same functionality over and over again. And that functionality (which is basically visible as web pages) is what gets encapsulated by a set of objects.

These ‘page objects’ may then be reused across the whole test suite. Nothing new or fancy, just good old engineering principles at work. Nevertheless, done well, they greatly reduce the duplication across the test suite. They also make development of tests fast and the test code becomes much more readable.

Thinking big – Atlassian-wide page objects

As it happened, the JIRA team was not the only one in Atlassian re-thinking its approach to functional tests. As Atlassian products are increasingly integrated and more and more features are delivered as plugins, the code gets more and more decentralized. Therefore duplication hits not only single product code base, but all plugins and frameworks that ship with products like JIRA, but are not hosted in the same source tree. Obviously they also need browser tests and so far have had no easy way of reusing or sharing any such functionality.

Company Wide Solution

The need to create a common Atlassian-wide solution became quite obvious. It was implemented as part of the atlassian-selenium project (version 2.0) and is now getting increasingly used within Atlassian.

The idea is that each product, plugin or integration framework will provide its own library of page objects based on the common framework and using the same underlying browser automation technology – we now standardize on the Selenium2/WebDriver API. That way any project can consume page objects from other projects that it integrates with or builds upon.

So what’s in?

There are two new important modules in atlassian-selenium 2.0. The fundament is the API module that contains the page binder framework. All page objects are assumed to be simple POJOs that, when instantiated, go through a well defined life cycle. They may optionally hook into this life cycle by defining methods with dedicated annotations. The default page binder implementation uses Google Guice and thus supports dependency injection via the @javax.annotation.Inject

. Page binder makes creating your page objects a breeze because it lets you remove lots of the usual boilerplate. This is true in particular for complex page objects like JIRA’s ViewIssuePage or IssueNavigatorPage that have lots of inner page objects and otheer dependencies. In the next part I will show some simple examples of how to write page-binder-compatible page objects. Beyond the page binder, the module also provides API of tested product, which represents concrete application under test (e.g. JIRA) and is entry point to all other components of the framework.

The second, optional, addition to atlassian-selenium 2.0 is the PageElement framework. It builts on top of the page binder API and Selenium2/WebDriver to provide functionality similar to WebDriver’s WebElement, but with a number of very useful additions. Some of the important features of PageElement are as follows:

  • it may be created even if the represented element on the page does not exist. It contains a boolean query to check whether the represented element does indeed exist. This is important difference to WebDriver that just throws exception when unable to locate a given element on the page
  • it rebinds automatically if the represented element on the page is reloaded/removed. That means it never throws StaleElementException as its cousin WebElement does
  • it has a timed version (TimedElement) whose methods map to PageElement almost 1 to 1, but return query-like objects instead of Java primitives (boolean/String). Those objects may then be exercised by a special Poller class to test if certain state of the page does or does not happen within given timeout. This is most useful in the case of AJAX requests and asynchronous JavaScript execution modifying the page – which we found one of the major causes of unreliability of our old Selenium 1 test suite.
  • it has specialized extensions: SelectElement, MultiSelectElement and CheckboxElement to easily manipulate corresponding HTML primitves on the page

As you can see, most of PageElement functionality is all about ease of use in rich web applications that make heavy use of JavaScript and AJAX, where elements on the page are added, displayed, hidden and removed dynamically and asynchronously to the executing test.

To be continued…

In the next part I will demonstrate the JIRA page objects, how to use them in your plugin’s tests and how to develop your own page objects. Stay tuned!

Interested in Atlassian Selenium 2.0? Check out the project page on studio.atlassian.com to learn more!