Test Automation with Selenium
Locators
Source code:
<div id="header-container"> <div id="header"> <div id="logotype">Logotype</div> </div> </div>
Locators for the element containing text Logotype:
id=logotype css=#logotype css=#header > div css=#header-container > div > div css=[id='logotype'] css=div[id*='ogo'] css=div[id^='logo'] css=div[id$='type'] xpath=//*[@id='logotype'] xpath=//*[@id='header']/div xpath=//*[@id='header-container']/div/div xpath=//*[@id='header-container']//div[@id='logotype'] xpath=//div[contains(@id, 'ogo')] xpath=//div[starts-with(@id, 'logo')] xpath=//div[ends-with(@id, 'type')]
Source code:
<div id="navigation-container"> <div id="navigation"> <ul> <li> <a href="index.html">Menu item 1</a> </li> <li> <a href="index.html">Menu item 2</a> </li> <li> <a href="index.html">Menu item 3</a> </li> <li> <a href="index.html">Menu item 4</a> </li> </ul> </div> </div>
Locators for the element containing text Menu item 3:
css=#navigation > ul > li:nth-of-type(3) > a css=#navigation > ul > li:nth-child(3) > a css=#navigation li:nth-child(3) > a xpath=//*[@id='navigation']/ul/li[3]/a xpath=//*[@id='navigation']/*/li[3]/a xpath=//*[@id='navigation']//li[3]/a
Source code:
<div id="main"> <div id="content"> <div id="hero"> <h1>Page title</h1> <p>Element 1. I aim to create simple yet engaging Web sites that enable users to easily achieve their objective. Read on to find out more about how I work, or get in touch if you've got a project you’d like to discuss or you'd just like to say hello.</p> </div> </div> </div>
Locators for the element containing text Page title:
css=h1 css=#hero > h1 xpath=//h1 xpath=//*[@id='hero']/h1 xpath=//*[text()='Page title']
Locators for the element containing text Element 1:
css=#hero > p css=h1 + p xpath=//p[starts-with(text(), 'Element 1')] xpath=//h1/following-sibling::p
Source code:
<div id="feature-container"> <div id="feature"> <div class="box"> <h3>Service 1</h3> <div class="note">Element 2. Lorem ipsum dolor sit amet erat. Fusce iaculis, turpis id enim. Mauris pretium, ipsum feugiat ultrices tortor et malesuada velit pretium.</div> <div> <img src="icon-check.svg" width="20" height="16" alt="" /> </div> </div> <div class="box"> <h3>Service 2</h3> <div class="note">Element 3. Nunc volutpat tempus malesuada. Donec consectetuer lobortis eu, vulputate fringilla. Morbi nisl mollis nunc volutpat id, libero.</div> <div> <img src="icon-check.svg" width="20" height="16" alt="" /> </div> </div> <div class="box"> <h3>Service 3</h3> <div class="note">Element 4. Aliquam erat ac ipsum. Integer aliquam purus. Quisque lorem tortor fringilla sed, vestibulum id, eleifend justo vel bibendum sapien.</div> <div> <img src="icon-check.svg" width="20" height="16" alt="" /> </div> </div> </div> </div>
Locators for the element containing text Service 3:
css=#feature > .box:nth-child(3) > h3 css=#feature > .box:last-child > h3 css=.box:nth-child(3) > h3 xpath=//*[@id='feature']/*[3][@class='box']/h3 xpath=//*[@id='feature']/*[@class='box'][last()]/h3 xpath=//*[@id='feature']/*[@class='box']/following-sibling::*[2] xpath=//*[3][@class='box']/h3 xpath=//*[@id='feature']//*[text()='Service 3']
Locators for the element containing text Element 3:
css=#feature > .box:nth-child(2) > .note xpath=//*[@id='feature']/*[2]/*[@class='note']
Source code:
<div id="newsflash-container"> <div id="newsflash"> <h2>Newsflash</h2> <p>Element 5. Donec consectetuer lobortis eu, vulputate fringilla. Morbi nisl mollis nunc volutpat id, libero.</p> <h3>News 1</h3> <div class="note">Element 6. Lorem ipsum dolor sit amet erat. Fusce iaculis, turpis id enim. Mauris pretium, ipsum feugiat ultrices tortor et malesuada velit pretium.</div> <h3>News 2</h3> <div class="note">Element 7. Nunc volutpat tempus malesuada. Donec consectetuer lobortis eu, vulputate fringilla. Morbi nisl mollis nunc volutpat id, libero.</div> <h3>News 3</h3> <div class="note">Element 8. Aliquam erat ac ipsum. Integer aliquam purus. Quisque lorem tortor fringilla sed, vestibulum id, eleifend justo vel bibendum sapien.</div> </div> </div>
Locators for the element containing text Element 7:
css=#newsflash .note:nth-of-type(2) css=#newsflash h3:nth-of-type(2) + div xpath=//*[@id='newsflash']/div[2][@class='note'] xpath=//*[@id='newsflash']/h3[2]/following-sibling::div xpath=//*[@class='note'][contains(text(), 'Element 7')]
Source code:
<div id="footer-container"> <div id="footer"> <div class="grid-9">Footnote 1. Donec consectetuer lobortis eu, vulputate fringilla. Morbi nisl mollis nunc volutpat id, libero.</div> <div class="grid-3 text-right">Footnote 2</div> </div> </div>
Locators for the element containing text Footnote 2:
css=#footer > div.grid-3 css=#footer > div:nth-child(2) css=#footer > div + div xpath=//*[@id='footer']/div[@class='grid-3 text-right'] xpath=//*[@id='footer']/div[2] xpath=//*[@id='footer']/*[2] xpath=//*[@id='footer']/div/following-sibling::div
Remote WebDriver
WebDriver can use local or external Selenium server, which we can run with the command:
java -jar selenium-server-standalone-2.42.2.jar
It will be required to add the appropriate classes in the source code:
import java.net.URL; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver;
Working with Selenium server also requires adding:
DesiredCapabilities capability = DesiredCapabilities.firefox(); capability.setBrowserName("firefox");
Present in the code, default Firefox driver:
driver = new FirefoxDriver();
Is replaced by the definition of an external Selenium server:
driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capability);
Support for other browsers is implemented using drivers developed independently of the Selenium project. You can attach them to Selenium server using command:
java -jar selenium-server-standalone-2.42.2.jar -Dwebdriver.chrome.driver=chromedriver.exe -Dwebdriver.ie.driver=IEDriverServer.exe
Running tests in other browsers is done by selecting the driver. For Chrome browser:
capability.setBrowserName("chrome");
Similarly for Internet Explorer browser:
capability.setBrowserName("internet explorer");
You can also specify the version of the browser and the platform on which you want to run the test:
capability.setBrowserName("firefox"); capability.setVersion("25.0.1"); capability.setPlatform(Platform.WINDOWS);
Selenium Server or the Node within the Selenium Grid has to be configured first.
Selenium Grid
Selenium Server as a Hub:
java -jar selenium-server-standalone-2.42.2.jar -role hub
The console is available at:
http://localhost:4444/grid/console
Register the Node with network address 192.168.0.50 at Selenium Hub with network address 192.168.0.10:
java -jar selenium-server-standalone-2.42.2.jar -role webdriver -hubHost 192.168.0.10 -host 192.168.0.50
Each Node can have its configuration of available browsers in a separate file. The contents of nodeConfig.json file:
{ "class": "org.openqa.grid.common.RegistrationRequest", "capabilities": [ { "platform": "WINDOWS", "seleniumProtocol": "WebDriver", "browserName": "firefox", "maxInstances": 4 }, { "platform": "WINDOWS", "seleniumProtocol": "WebDriver", "browserName": "chrome", "maxInstances": 4 }, { "platform": "WINDOWS", "seleniumProtocol": "WebDriver", "browserName": "internet explorer", "version": 8, "maxInstances": 1 } ], "configuration": { "register": true, "role": "node", "hubHost": "192.168.0.10", "host": "192.168.0.50" } }
Use of a configuration file:
java -jar selenium-server-standalone-2.42.2.jar -role webdriver -nodeConfig nodeConfig.json
Screenshots
Needed classes:
import java.util.Calendar; import java.text.SimpleDateFormat; import java.io.File; import org.apache.commons.io.FileUtils;
In the source code, in desired location you must add:
Calendar calendar = Calendar.getInstance(); SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd_hhmmss"); File screenshotFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); FileUtils.copyFile(screenshotFile, new File("C:\\Selenium\\screenshot_" + formater.format(calendar.getTime()) + ".png"));
In case of remote requests using RemoteWebDriver, getScreenshotAs method is not implemented and requires the creation of a virtual driver:
WebDriver augmentedDriver = new Augmenter().augment(driver); File screenshotFile = ((TakesScreenshot)augmentedDriver).getScreenshotAs(OutputType.FILE);
The definition of the driver should be placed before the screenshot command.
TestNG
Running tests using TestNG requires changes in the code. Currently, are imported classes from JUnit package:
import org.junit.*; import static org.junit.Assert.*;
We will replace them with classes from TestNG package:
import org.testng.annotations.*; import static org.testng.Assert.*;
At the same time JUnit annotations:
@Before @Test @After
We will replace with annotations suitable for TestNG:
@BeforeTest @Test @AfterTest
After such changes, development environment, will have the ability to run tests using TestNG.
The tests can be parameterised using a separate configuration file. Parameters are passed using annotation that needs to be added to the code:
@Parameters
Parameters will be available as variables.
The contents of testng.xml file:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="MySuite" verbose="1" thread-count="1" parallel="tests"> <tests> <test name="Windows + Firefox"> <parameters> <parameter name="platform" value="WINDOWS" /> <parameter name="browser" value="firefox" /> </parameters> <classes> <class name="MyTest" /> </classes> </test> <test name="Windows + Chrome"> <parameters> <parameter name="platform" value="WINDOWS" /> <parameter name="browser" value="chrome" /> </parameters> <classes> <class name="MyTest" /> </classes> </test> <test name="Windows + Internet Explorer"> <parameters> <parameter name="platform" value="WINDOWS" /> <parameter name="browser" value="internet explorer" /> </parameters> <classes> <class name="MyTest" /> </classes> </test> <test name="Linux + Firefox"> <parameters> <parameter name="platform" value="LINUX" /> <parameter name="browser" value="firefox" /> </parameters> <classes> <class name="MyTest" /> </classes> </test> <test name="Linux + Chrome"> <parameters> <parameter name="platform" value="LINUX" /> <parameter name="browser" value="chrome" /> </parameters> <classes> <class name="MyTest" /> </classes> </test> </tests> </suite>
Edit Run configuration, change option from Class to Suite and specify the full path to testng.xml file.
Apache Ant
Ant tool allows you to automate build and run tests from the command line. It requires a separate build.xml configuration file:
<project name="TestNGTest" default="test" basedir="."> <taskdef name="testng" classname="org.testng.TestNGAntTask"> <classpath> <pathelement location="lib/testng-6.8.jar"/> </classpath> </taskdef> <property name="testdir" location="test" /> <property name="srcdir" location="src" /> <property name="libdir" location="lib" /> <property name="full-compile" value="true" /> <path id="classpath.base"/> <path id="classpath.test"> <fileset dir="${libdir}"> <include name="**/*.jar" /> </fileset> <pathelement location="${testdir}" /> <pathelement location="${srcdir}" /> <path refid="classpath.base" /> </path> <target name="clean"> <delete verbose="${full-compile}"> <fileset dir="${testdir}" includes="**/*.class" /> </delete> </target> <target name="compile" depends="clean"> <javac srcdir="${srcdir}" destdir="${testdir}" verbose="${full-compile}"> <classpath refid="classpath.test"/> </javac> </target> <target name="test" depends="compile"> <testng outputdir="${testdir}" classpathref="classpath.test"> <xmlfileset dir="${srcdir}" includes="testng.xml"/> </testng> </target> </project>
Configuration defines the structure of the project and the location of the libraries, source files and files related to running the test. The structure of the workspace:
workspace ├── build.xml ├── lib │ ├── selenium-server-standalone-2.42.2.jar │ └── testng-6.8.jar ├── src │ ├── MyTest.java │ └── testng.xml └── test
To run the test, execute ant program in the workspace directory.
The source code of MyTest.java file with sample test that uses the parameters:
import java.net.URL; import java.util.concurrent.TimeUnit; import org.testng.annotations.*; import static org.testng.Assert.*; import org.openqa.selenium.*; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; public class MyTest { private WebDriver driver; private String baseUrl; private StringBuffer verificationErrors = new StringBuffer(); @Parameters({"platform", "browser"}) @BeforeTest(alwaysRun = true) public void setUp(String platform, String browser) throws Exception { DesiredCapabilities capability = new DesiredCapabilities(); if (browser.equals("chrome")) { capability = DesiredCapabilities.chrome(); capability.setBrowserName("chrome"); } else if (browser.equals("internet explorer")) { capability = DesiredCapabilities.internetExplorer(); capability.setBrowserName("internet explorer"); } else { capability = DesiredCapabilities.firefox(); capability.setBrowserName("firefox"); } if (platform.equals("LINUX")) { capability.setPlatform(Platform.LINUX); } else if (platform.equals("MAC")) { capability.setPlatform(Platform.MAC); } else { capability.setPlatform(Platform.WINDOWS); } driver = new RemoteWebDriver(new URL("http://192.168.0.10:4444/wd/hub"), capability); baseUrl = "https://www.google.com"; driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void testMyTest() throws Exception { driver.get(baseUrl + "/"); driver.findElement(By.id("gbqfq")).clear(); driver.findElement(By.id("gbqfq")).sendKeys("Selenium"); driver.findElement(By.id("gbqfb")).click(); assertEquals("Selenium - Web Browser Automation", driver.findElement(By.xpath("//ol[@id='rso']//li//h3/a")).getText()); } @AfterTest public void tearDown() throws Exception { driver.quit(); String verificationErrorString = verificationErrors.toString(); if (!"".equals(verificationErrorString)) { fail(verificationErrorString); } } }
Page Objects
This design pattern has become popular in test automation, because it facilitates tests maintenance.
The Source code of SimpleGoogleSearchTest.java file:
import org.junit.*; import static org.junit.Assert.*; import org.openqa.selenium.*; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class SimpleGoogleSearchTest { private WebDriver driver; private String baseUrl; @Before public void setUp() throws Exception { driver = new FirefoxDriver(); baseUrl = "https://www.google.com"; } @Test public void SimpleGoogleSearchTest() { driver.get(baseUrl + "/"); driver.findElement(By.id("gbqfq")).clear(); driver.findElement(By.id("gbqfq")).sendKeys("Selenium"); driver.findElement(By.id("gbqfb")).click(); WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//ol[@id='rso']//li//h3/a"))); assertEquals("Selenium - Web Browser Automation", driver.findElement(By.xpath("//ol[@id='rso']//li//h3/a")).getText()); } @After public void tearDown() { driver.quit(); } }
The Source code of GoogleSearchTest.java file:
import org.junit.*; import static org.junit.Assert.*; import org.openqa.selenium.*; import org.openqa.selenium.firefox.FirefoxDriver; public class GoogleSearchTest { private WebDriver driver; @Before public void setUp() { driver = new FirefoxDriver(); } @Test public void GoogleSearchTest() { GoogleHomePage homePage = GoogleHomePage.open(driver); GoogleSearchResultsPage resultsPage = homePage.search("Selenium"); assertEquals("Selenium - Web Browser Automation", resultsPage.getTopResult()); } @After public void tearDown() { driver.quit(); } }
The Source code of GoogleHomePage.java file:
import org.openqa.selenium.*; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class GoogleHomePage { private WebDriver driver; @FindBy(id="gbqfq") private WebElement searchField; @FindBy(id="gbqfb") private WebElement searchButton; public GoogleHomePage(WebDriver driver) { this.driver = driver; } public static GoogleHomePage open(WebDriver driver) { driver.get("https://www.google.com/"); return PageFactory.initElements(driver, GoogleHomePage.class); } public GoogleSearchResultsPage search(String searchTerm) { searchField.clear(); searchField.sendKeys(searchTerm); searchButton.click(); return PageFactory.initElements(driver, GoogleSearchResultsPage.class); } }
The Source code of GoogleSearchResultsPage.java file:
import org.openqa.selenium.*; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; public class GoogleSearchResultsPage { private WebDriver driver; @FindBy(xpath="//ol[@id='rso']//li//h3/a") private WebElement topResultTitle; public GoogleSearchResultsPage(WebDriver driver) { this.driver = driver; } public String getTopResult() { WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.visibilityOf(topResultTitle)); return topResultTitle.getText(); } }