In our introductory post, A Survey of the Automated Testing Landscape, we defined the problems we often find in automated testing. In this series, we will walk through an intelligent example of a Selenium test project that uses common design patterns and object oriented principles to help solve the problem of Technical Debt. This is not to say that this example is the perfect solution that will solve all of your problems. Remember from the introductory post that unicorns do not exist. Excellence takes planning, preparation, hard work, and execution.
What are we going to do?
Gouda cheese is arguably the best cheese on the planet. Given this fact, a natural page flow could be as follows:
Remember, we want to exercise our rules:
- No Selenium imports in our scripts
- When we need to wait, wait intelligently
We want our tests to remain simple so we can grasp the big picture. Our first big picture item will address the first rule. Why do we care if we have Selenium imports in our tests?
Managing Technical Debt
In our specific case, we will be talking about decreasing script creation and maintenance costs. When we try to take shortcuts, we make decisions that we will have to pay for later. Most of the time, this results in throwing away old scripts because it would take longer to fix them than to recreate the subset we really need.
In this example project we will be using a Page Object Model. These model classes will represent our pages and will expose elements of the page as well as behavior to our tests. Our tests will simply manipulate the page model. This is a huge win for us. It primarily gives us two things. First, it gives us a separation between the test script and the application itself. When the application changes, the model will change, but the tests may not have to. When the tests do have to change, the change is less impactful.
Second, it gives us specialization in resources. Separating the model from the tests allows test architects to do the heavy lifting in the model and test developers to write the tests. If done correctly, the tests will be very human readable. If a test developer can write a Hello World app, then they can use a well-written model to test an application. Consider the diagram below.
We have a script that will consume and use page models representing each of the pages. These page models are aware of Selenium and use the appropriate Selenium selectors to find the elements on the page that we care about.
The BasePage is an abstract parent class that allows each concrete Page class to access the WebDriverResource (more on this later). The BasePage class also allows us to access multiple windows and create wait conditions. If you do not have an ajax heavy application, you may not have to consider waits at all.
The WebDriverResource is a custom object that uses a singleton pattern to give the page models one and only one WebDriver and WebDriverWait object. Abstracting this work out to the WebDriverResource class allows the page model to be unaware of how the WebDriver is created or even what kind of a browser it is.
What is next?
In our next post, we will get into the code and explore how these classes are tied together. You can find the example project out on GitHub at: Selenium Example Project.