Test Automation with Selenium

From Training Material
Revision as of 13:35, 1 July 2014 by Łukasz Walec (talk | contribs) (→‎Selenium Grid)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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();
	}
}