This post originated from an RSS feed registered with Java Buzz
by Ross Mahony.
Original Post: Selenium JUnit Testing
Feed Title: Monster Sandwich - Java, Spring, Hibernate, JPA, JEE, Scala
Feed URL: http://monstersandwich.blogspot.com/feeds/posts/default?alt=rss
Feed Description: A practical site with discussions on a wide range of Java topics where I have tried to include best practices. I try to include practical working examples that anyone can download, install and run. I would love to open discussion to other developers to collaborate with and to learn.
I recently put together a Selenium JUnit testing project to automate testing for a web project I am working on. Having never done or worked on a Selenium project before I did a little research into finding out how it works and what approach best suits me. The following describes a few things I decided upon.
Selenium is used to automate tests through a web browser. Jenkins was used to run the tests from this project after a successful build of and deploy of the web application.
Useful Resources
Here are a couple of useful links where you can find more information on Selenium:
I decided on the following design principles for the Selenium test project:
Each test should run in its own session
Starting a new session means closing the browser and opening it again. The disadvantage to this is that it takes longer to complete a test however the advantage means that any previously run tests don’t pollute the session for the currently running test.
The change to the above also means that within Eclipse you can choose to run all tests by running the test suite or you could also selectively choose an individual test to run as each test can be run independently from each other. You could do the same within Buildr or through the command line.
Spring Support
Spring support has been added to the test project classes so that we could take advantage of the following:
Dependency injection, example would be connection to the database
The ability to run the test cases for different environments based on a spring profile setting
Use of Spring support classes, example: jdbctemplate
Important notes to keep in mind:
Tests are NOT transactional. Selenium opens a browser and users the applications configured transaction manager.
DbUnit
DbUnit is a JUnit extension targeted at database-driven projects that puts your database into a known state between test runs. DbUnit has the ability to export and import your database data to and from XML datasets.
The tests should not rely on data already in the database since I did not have a dedicated test database and the state of the database is not guaranteed. In certain scenarios we could write tests that use data from the database where we know the data is not going to change. DbUnit is used to populate the database with test data before we run the test and then it removes the data from the database at the end of the test run.
Please refer to the following link in finding out the recommended best practices for DbUnit:
The test project is also using the Spring Test DbUnit project to integrated with the Spring testing framework. It allows you to setup and teardown database tables using simple annotations as well as checking expected table contents once a test completes.
Page Object Model Design Pattern
The Page Object Model is a design pattern to create Object Repository for web UI elements. The following principles apply:
Under this model, for each web page in the application there should be corresponding page class
This Page class will find the WebElements of that web page and also contains Page methods which perform operations on those WebElements
Name of these methods should be given as per the task they are performing i.e., if a loader is waiting for payment gateway to be appear, POM method name can be waitForPaymentScreenDisplay()
Here are some of the advantages of applying the Page Object Model Design Pattern:
Page Object Patten says operations and flows in the UI should be separated from verification. This concept makes our code clean and easy to understand
Second benefit is the object repository is independent of testcases, so we can use the same object repository for a different purpose with different tools. For example, we can integrate POM with TestNG/JUnit for functional testing and at the same time with JBehave/Cucumber for acceptance testing
The number of lines of code are reduced and optimized because of the reusable page methods in the POM classes
Methods get more realistic names which can easily be mapped to the operation happening in the UI, i.e. if after clicking on the button we land on the home page, the method name could be 'gotoHomePage()'
One problem is that every time we call a method on the WebElement the driver will go and find it on the current page again. In an AJAX-heavy application this is what you would like to happen, but in the some cases we know that the element is always going to be there and won't change. We also know that we won't be navigating away from the page and returning. It would be handy if we could "cache" the element once we'd looked it up:
// The element is now looked up using the name attribute, // and we never look it up once it has been used the first time @FindBy(name="myFieldId") @CacheLookup private WebElement myField;
For more information on the PageFactory please read the following link:
Waiting for an element to exist before looking it up
Selenium tests require a browser to open and a page to load before the code attempts to lookup the expected elements in the page. Selenium has a number of settings to try and cater for this scenario:
Implicit Wait
We can tell Selenium that we would like it to wait for a certain amount of time before throwing an exception when it cannot find the element on the page. Implicit waits will be in place for the entire time the browser is open. This means that any search for elements on the page could take the time the implicit wait is set for.
During Implicit wait if the Web Driver cannot find it immediately because of its availability, the WebDriver will wait for the mentioned time and it will not try to find the element again during the specified time period. Once the specified time is over, it will try to search the element once again the last time before throwing exception. The default setting is zero. Once we set a time, the Web Driver waits for the period of the WebDriver object instance.
FluentWait
Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition. Furthermore, the user may configure the wait to ignore specific types of exceptions whilst waiting, such as NoSuchElementExceptions when searching for an element on the page.
If you have an element which sometime appears in just 1 second and some time it takes minutes to appear than it is better to use fluent wait, as this will try to find the element again and again until it finds it or until the final timer runs out.
// Waiting 30 seconds for an element to be present on the page, checking // for its presence once every 5 seconds. Wait wait = new FluentWait(driver) .withTimeout(30, SECONDS) .pollingEvery(5, SECONDS) .ignoring(NoSuchElementException.class);
The WebDriverWait is a specialization of FluentWait that uses WebDriver instances. It is more extendible in the means that you can set it up to wait for any condition you might like. Usually, you can use some of the prebuilt ExpectedConditions to wait for elements to become clickable, visible, invisible, etc.
There can be an instance when a particular element takes more than a minute to load. In that case you don't want to set a huge time to Implicit wait because then your browser will wait the same time for every element. To avoid that situation you can put a separate time on the required element.
WebDriverWait wait = new WebDriverWait(driver, 10); WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
Selenium Locator Strategies
There are 8 locators that Selenium’s commands support:
id - Ids are the most preferred way to locate elements on a page, fast and reliable way to locate elements
name - An efficient way to locate an element but unlike Ids, name attributes don’t have to be unique in a page
identifier - Combination of id and name, first checks the @id attribute and if no match is found it tries the @name attribute
css - Locate an element by using CSS selectors to find the element in the page
xpath - Locate an element using an XPath query
link - Locate a link element ("a" tag) by the text used within the link tag
dom - Locate elements that match the JavaScript expression referring to an element in the DOM of the page
// find all the table cells that match the css selector @FindBy(css="div[id='myFormId:myTableId'] > div > table > tbody > tr > td > span")
// same as above but won't only check the next element in tree and will keep searching until a match is found @FindBy(css="div[id='myFormId:myTableId'] table td span")
// find all the table cells that match the xpath query @FindBy(xpath="//div[@id='myFormId:myTableId']/div/table/tbody/tr/td/span")
// same as above but won't only check the next element in tree and will keep searching until a match is found @FindBy(xpath="//div[@id='myFormId:myTableId']//table//td//span")
// to find a cell that contains text @FindBy(xpath="//div[@id='myFormId:myTableId']//table//td//span[contains(text(),'Row 1 - Value of column 1')]")
Find an element whose ID matches part of an expression, the following examples use an "a" link element but can be any valid html tag: