Java Web Automation (Cucumber)
Selenium, Page Objects, Cucumber-JVM and Gradle in Java
Introduction
In this post, we will write an automated test that utilises Selenium WebDriver, Cucumber-JVM and Gradle. We will also make use of Selenium Grid in Gradle.
In this guide we will automate a browser navigating Cucumber. We will start off with a very basic test, but will then slowly build on top of it to incorporate Cucumber and Page Objects.
Installation
Gradle
Download and extract Gradle. You will also have to set your add GRADLE_HOME/bin to your PATH environment variable.
Ensure that you have successfully installed Gradle:
bash-3.2$ gradle -v ------------------------------------------------------------ Gradle 2.10 ------------------------------------------------------------
In a directory of your choice, you are now going to use Gradle’s Build Init Plugin to bootstrap the process of creating a new Gradle build:
bash-3.2$ gradle init --type java-library :wrapper :init BUILD SUCCESSFUL
As part of this command, you would now also have automatically generated Gradle Wrapper. Gradle Wrapper allows anybody to work on your project without having to install Gradle. It ensures that the right version of Gradle that the build was designed for is shipped as part of the project repository. Remove Library.java
from /src/main/java/
and LibraryTest.java
from /src/test/java/
`, this will be replaced by our own implementation later.
Selenium Grid
We will now be able touse Gradle to manage Selenium Grid. Update your build.gradle
file to contain the following:
// apply the java plugin to add support for Java apply plugin: 'java' // in this section declare where to find the dependencies of your project repositories { jcenter() } // in this section declare the dependencies for your production and test code dependencies { compile "org.seleniumhq.selenium:selenium-support:2.53.1" compile "org.seleniumhq.selenium:selenium-firefox-driver:2.53.1" compile "org.seleniumhq.selenium:selenium-chrome-driver:2.53.1" compile "org.seleniumhq.selenium:selenium-remote-driver:2.53.1" compile "org.seleniumhq.selenium:selenium-server:2.53.1" compile "info.cukes:cucumber-java:1.2.4" compile "info.cukes:cucumber-core:1.2.4" compile "info.cukes:cucumber-junit:1.2.4" compile 'info.cukes:gherkin:2.12.2' testCompile 'junit:junit:4.12' } task startSeleniumHub() << { ant.java(classname : 'org.openqa.grid.selenium.GridLauncher', fork:true, classpath : configurations.runtime.asPath) { arg(value : '-role') arg(value : 'hub') } } task startSeleniumNode() << { ant.java(classname : 'org.openqa.grid.selenium.GridLauncher', fork:true, classpath : configurations.runtime.asPath) { arg(value : '-role') arg(value : 'node') arg(value : '-hub') arg(value : 'http://localhost:4444/grid/register') } }
A grid consists of a single hub, and one or more nodes. Both are started using the selenium-server.jar executable.
The hub receives a test to be executed along with information on which browser and platform where the test should be run. It knows the configuration of each node that has been registered to the hub. Using this information it selects an available node that has the requested browser-platform combination to run the test against. In this tutorial, will run Selenium Grid locally.
First, start the hub:
bash-3.2$ ./gradlew startSeleniumHub
Second, start the node:
bash-3.2$ ./gradlew startSeleniumNode
Ensure your setup has been successful by navigating to http://127.0.0.1:4444/grid/console.
Introducing Selenium
We can now write a very simple test that utilises WebDriver by creating ExampleTest.java
in /src/test/java/
. We will make use of JUnit’s @Before
, @After
and @Test
annoations:
import java.net.MalformedURLException; import java.net.URL; import org.junit.*; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import static org.junit.Assert.assertTrue; public class ExampleTest { WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @After public void tearDown() { driver.quit(); } @Test public void thisIsTheActualTest() { driver.get("https://cucumber.io/"); WebElement element = driver.findElement(By.linkText("Docs")); element.click(); assertTrue(driver.getTitle().contains("Cucumber")); } }
Now, try running the test and observe that Firefox is launched and the test is run:
bash-3.2$ ./gradlew clean build :clean :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :jar :assemble :compileTestJava :processTestResources UP-TO-DATE :testClasses :test :check :build BUILD SUCCESSFUL Total time: 18.906 secs
Let’s now update ExampleTest.java
to utilise RemoteWebDriver and the Selenium Grid that you have started in the background:
import java.net.MalformedURLException; import java.net.URL; import org.junit.*; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import static org.junit.Assert.assertTrue; public class ExampleTest { DesiredCapabilities capabilities; RemoteWebDriver driver; @Before public void setUp() throws MalformedURLException { capabilities = DesiredCapabilities.firefox(); capabilities.setCapability("browserName", "firefox"); driver = new RemoteWebDriver(new URL("http://127.0.0.1:4444/wd/hub"), capabilities); } @After public void tearDown() { driver.quit(); } @Test public void thisIsTheActualTest() { driver.get("https://cucumber.io/"); WebElement element = driver.findElement(By.linkText("Docs")); element.click(); assertTrue(driver.getTitle().contains("Cucumber")); } }
Now, try running the test and observe that Firefox is launched and the test is run:
bash-3.2$ ./gradlew clean build :clean :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :jar :assemble :compileTestJava :processTestResources UP-TO-DATE :testClasses :test :check :build BUILD SUCCESSFUL Total time: 24.002 secs
Introducing Cucumber
Let’s next introduce Cucumber-JVM! First, create the file Cucumber.feature
in /src/test/resources/
and place the following contents:
Feature: Cucumber Home Page Scenario: Docs Given I navigate to "https://cucumber.io/" When I take a look at the Docs Then I see a browser title containing "Cucumber"
Create the file CucumberRunner.java
in /src/test/java/
and place the following contents to act as the glue for our implementation:
import cucumber.api.CucumberOptions; import org.junit.runner.RunWith; @CucumberOptions(features = {"src/test/resources"}) @RunWith(cucumber.api.junit.Cucumber.class) public class CucumberRunner { }
Rename the file ExampleTest.java
to CucumberSteps.java
, and move the file to /src/test/java/steps/
and place the following contents. We will be making use of Cucumber-JVM’s annotations @Before
and @After
as well as @Given
, @When
and @Then
to match the steps written in Cucumber.feature
:
package steps; import cucumber.api.java.After; import cucumber.api.java.Before; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import java.net.MalformedURLException; import java.net.URL; import static org.junit.Assert.assertTrue; public class CucumberSteps { RemoteWebDriver driver; DesiredCapabilities capabilities; @Before public void setUp() throws MalformedURLException { capabilities = DesiredCapabilities.firefox(); capabilities.setCapability("browserName", "firefox"); driver = new RemoteWebDriver(new URL("http://127.0.0.1:4444/wd/hub"), capabilities); } @After public void tearDown() { driver.close(); } @Given("^I navigate to \"([^\"]*)\"$") public void i_navigate_to(String page) throws Throwable { driver.get(page); } @When("^I take a look at the Docs$") public void i_take_a_look_at_the_docs() throws Throwable { WebElement element = driver.findElement(By.linkText("Docs")); element.click(); } @Then("^I see a browser title containing \"([^\"]*)\"$") public void i_see_a_browser_title_containing(String text) throws Throwable { assertTrue(driver.getTitle().contains(text)); } }
Now, try running the test and observe that Firefox is launched and the test is run:
bash-3.2$ ./gradlew clean test :clean :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources :testClasses :test BUILD SUCCESSFUL Total time: 22.261 secs
Introducing Page Objects
Let’s now introduce the concept of Page Objects. Create the file CucumberHomePage.java
in /src/test/java/pages/
and place the following contents using the @FindBy
annotation to look for the element:
package pages; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; public class CucumberHomePage { @FindBy(linkText = "Docs") WebElement docsLink; public void clickDocsLink() { docsLink.click(); } }
In the file CucumberSteps.java
update the following contents to import the new Page Object that is initialised by PageFactory:
package steps; import cucumber.api.java.After; import cucumber.api.java.Before; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import java.net.MalformedURLException; import java.net.URL; import pages.CucumberHomePage; import org.openqa.selenium.support.PageFactory; import static org.junit.Assert.assertTrue; public class CucumberSteps { RemoteWebDriver driver; DesiredCapabilities capabilities; CucumberHomePage cucumberHomePage; @Before public void setUp() throws MalformedURLException { capabilities = DesiredCapabilities.firefox(); capabilities.setCapability("browserName", "firefox"); driver = new RemoteWebDriver(new URL("http://127.0.0.1:4444/wd/hub"), capabilities); cucumberHomePage = PageFactory.initElements(driver, CucumberHomePage.class); } @After public void tearDown() { driver.close(); } @Given("^I navigate to \"([^\"]*)\"$") public void i_navigate_to(String page) throws Throwable { driver.get(page); } @When("^I take a look at the Docs$") public void i_take_a_look_at_the_docs() throws Throwable { // WebElement element = driver.findElement(By.linkText("Docs")); // element.click(); cucumberHomePage.clickDocsLink(); } @Then("^I see a browser title containing \"([^\"]*)\"$") public void i_see_a_browser_title_containing(String text) throws Throwable { assertTrue(driver.getTitle().contains(text)); } }
Now, try running the test and observe that Firefox is launched and the test is run:
bash-3.2$ ./gradlew clean test :clean :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources :testClasses :test BUILD SUCCESSFUL Total time: 19.391 secs
Introducing DriverFactory
Let’s now create the ability to share an instance of a RemoteWebDriver across different step definition and feature files. Create the file DriverFactory.java
in /src/test/java/utils/
and place the following contents:
package utils; import cucumber.api.java.After; import cucumber.api.java.Before; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.PageFactory; import pages.CucumberHomePage; import java.net.MalformedURLException; import java.net.URL; public class DriverFactory { public static RemoteWebDriver driver; DesiredCapabilities capabilities; @Before public void setUp() throws MalformedURLException { capabilities = DesiredCapabilities.firefox(); capabilities.setCapability("browserName", "firefox"); driver = new RemoteWebDriver(new URL("http://127.0.0.1:4444/wd/hub"), capabilities); } @After public void tearDown() { driver.close(); } }
Now update CucumberSteps.java
to make use of DriverFactory with the following contents:
package steps; import cucumber.api.java.After; import cucumber.api.java.Before; import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import java.net.MalformedURLException; import java.net.URL; import pages.CucumberHomePage; import utils.DriverFactory; import org.openqa.selenium.support.PageFactory; import static org.junit.Assert.assertTrue; public class CucumberSteps { RemoteWebDriver driver = DriverFactory.driver; CucumberHomePage cucumberHomePage = PageFactory.initElements(driver, CucumberHomePage.class); @Given("^I navigate to \"([^\"]*)\"$") public void i_navigate_to(String page) throws Throwable { driver.get(page); } @When("^I take a look at the Docs$") public void i_take_a_look_at_the_docs() throws Throwable { // WebElement element = driver.findElement(By.linkText("Docs")); // element.click(); cucumberHomePage.clickDocsLink(); } @Then("^I see a browser title containing \"([^\"]*)\"$") public void i_see_a_browser_title_containing(String text) throws Throwable { assertTrue(driver.getTitle().contains(text)); } }
Now, try running the test and observe that Firefox is launched and the test is run:
bash-3.2$ ./gradlew clean test :clean :compileJava UP-TO-DATE :processResources UP-TO-DATE :classes UP-TO-DATE :compileTestJava :processTestResources :testClasses :test BUILD SUCCESSFUL Total time: 23.717 secs
Full Example
https://github.com/the-creative-tester/java-web-browser-automation-example