A guide to clear assertions with Hamcrest

A good practice in test automation is the use of Descriptive And Meaningful Phrases (also known as DAMP). This means that our tests clearly tell us what they do in language that is relevant to the domain. My goal for this article is to guide you through the steps to making descriptive and meaningful assertions with Hamcrest. I will do so by tackling the following issues.

  1. Readability of our assertions
  2. Completeness of the output of our assertions
  3. Duplication of assertion fail information in our tests

Let’s assume we have a class SpeedCamera with a method isPictureTaken which returns a boolean. We’ll skip the setup of the test for brevity’s sake. Let’s write the following JUnit assertion.


public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertFalse(speedCamera.isPictureTaken()); 
} 
// failure output: java.lang.AssertionError 
// org.junit.Assert.fail(Assert.java:86) .....long stack trace
1
2
3
4
5
6
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertFalse(speedCamera.isPictureTaken()); 
} 
// failure output: java.lang.AssertionError 
// org.junit.Assert.fail(Assert.java:86) .....long stack trace

Readability
Note that the assertion is not very readable and its failure output is incomplete. We’ll take our first step in solving the readability issue with Hamcrest.


public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertThat(speedCamera.isPictureTaken(), equalTo(false)); 
} 
/* failure output: java.lang.AssertionError: 
Expected: <false>
but: was <true>*/
1
2
3
4
5
6
7
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertThat(speedCamera.isPictureTaken(), equalTo(false)); 
} 
/* failure output: java.lang.AssertionError: 
     Expected: <false>
     but: was <true>*/

Completeness
Our test reads like a sentence, and even our failure output has improved a bit. But we miss important failure information for our test report. If we were to read the test report, for instance after running a test suite, we would not know which method expected false but was true without having to go back to the test code. We can solve this isue by adding a description to our test like this.


public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53);
assertThat("isPictureTaken should have returned false",
speedCamera.isPictureTaken(), equalTo(false));
} 
/*failure output: java.lang.AssertionError: isPictureTaken should have returned false 
Expected: <false> 
but: was <true>*/ 
1
2
3
4
5
6
7
8
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53);
assertThat("isPictureTaken should have returned false",
speedCamera.isPictureTaken(), equalTo(false));
} 
/*failure output: java.lang.AssertionError: isPictureTaken should have returned false 
    Expected: <false> 
    but: was <true>*/ 

Duplication
Our test failure output now tells us which method returns the wrong value. But if we continue to use this approach, we create the problem of duplication of our test failure information as we can see in this example.


public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53);
assertThat("isPictureTaken should have returned false", 
speedCamera.isPictureTaken(), equalTo(false));
}
public void shouldTakePicture() {
vehicle.passSpeedCamera(54);
assertThat("isPictureTaken should have returned true",
speedCamera.isPictureTaken(), equalTo(true)); 
}
// ad infinitum...
 1
2
3
4
5
6
7
8
9
10
11
12
13
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53);
assertThat("isPictureTaken should have returned false", 
speedCamera.isPictureTaken(), equalTo(false));
}
public void shouldTakePicture() {
vehicle.passSpeedCamera(54);
assertThat("isPictureTaken should have returned true",
speedCamera.isPictureTaken(), equalTo(true)); 
}
// ad infinitum...

But we can solve the duplication issue by implementing customised BaseMatchers. Let’s assume our SpeedCamera has three methods getCorrectedSpeed, isPictureTaken and isLicenseRevoked. We’ll use the TypeSafeMatcher because it’s straightforward in usage. Check out the Hamcrest API for more options.


public class SpeedCameraMatchers {
public static BaseMatcher<SpeedCamera> hasCorrectedSpeedTo(final int correctedSpeed) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("The camera should have corrected the speed to ")
.appendValue(correctedSpeed);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" alas, the camera corrected it to ")
.appendValue(camera.getCorrectedSpeed());
}
public boolean matchesSafely(final SpeedCamera camera) {
return correctedSpeed == camera.getCorrectedSpeed();
}
};
}
public static BaseMatcher<SpeedCamera> hasTakenAPicture(final boolean hasTakenPicture) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("Taking a picture should have returned ")
.appendValue(hasTakenPicture);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" returned ")
.appendValue(camera.isPictureTaken());
}
public boolean matchesSafely(final SpeedCamera camera) {
return hasTakenPicture == camera.isPictureTaken();
}
};
}
public static BaseMatcher<SpeedCamera> hasRevokedLicense(final boolean hasRevokedLicense) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("Revoking license should have returned ")
.appendValue(hasRevokedLicense);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" returned ")
.appendValue(camera.isLicenseRevoked());
}
public boolean matchesSafely(final SpeedCamera camera) {
return hasRevokedLicense == camera.isLicenseRevoked();
}
};
} 
} 
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class SpeedCameraMatchers {
public static BaseMatcher<SpeedCamera> hasCorrectedSpeedTo(final int correctedSpeed) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("The camera should have corrected the speed to ")
.appendValue(correctedSpeed);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" alas, the camera corrected it to ")
.appendValue(camera.getCorrectedSpeed());
}
public boolean matchesSafely(final SpeedCamera camera) {
return correctedSpeed == camera.getCorrectedSpeed();
}
};
}
public static BaseMatcher<SpeedCamera> hasTakenAPicture(final boolean hasTakenPicture) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("Taking a picture should have returned ")
.appendValue(hasTakenPicture);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" returned ")
.appendValue(camera.isPictureTaken());
}
public boolean matchesSafely(final SpeedCamera camera) {
return hasTakenPicture == camera.isPictureTaken();
}
};
}
public static BaseMatcher<SpeedCamera> hasRevokedLicense(final boolean hasRevokedLicense) { 
return new TypeSafeMatcher<SpeedCamera>() {
public void describeTo(final Description description) {
description.appendText("Revoking license should have returned ")
.appendValue(hasRevokedLicense);
}
public void describeMismatchSafely(final SpeedCamera camera, final Description mismatchDescription) {
mismatchDescription.appendText(" returned ")
.appendValue(camera.isLicenseRevoked());
}
public boolean matchesSafely(final SpeedCamera camera) {
return hasRevokedLicense == camera.isLicenseRevoked();
}
};
} 
} 

Now our assertions are even more readable, their output provides us with complete information and we’re able to reuse them without duplicating code.


public void shouldCorrectSpeedToZero() { 
vehicle.passSpeedCamera(0); 
assertThat(speedCamera, hasCorrectedSpeedTo(0)); 
} 
/*failure output: java.lang.AssertionError: 
Expected: The camera should have corrected the speed to <0> 
but: alas, the camera corrected it to <-3>*/
public void shouldCorrectSpeedToHundred() { 
vehicle.passSpeedCamera(105); 
assertThat(speedCamera, hasCorrectedSpeedTo(100)); 
} 
/*failure output: java.lang.AssertionError: 
Expected: The camera should have corrected the speed to <100> 
but: alas, the camera corrected it to <99>*/
public void shouldTakePicture() { 
vehicle.passSpeedCamera(54); 
assertThat(speedCamera, hasTakenAPicture(true)); 
} 
/*failure output: java.lang.AssertionError:
Expected: Taking a picture should have returned <true> 
but: returned <false>*/
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertThat(speedCamera, hasTakenAPicture(false)); 
} 
/*failure output: java.lang.AssertionError:
Expected: Taking a picture should have returned <false> 
but: returned <true>*/
public void shouldRevokeLicense() { 
vehicle.passSpeedCamera(105); 
assertThat(speedCamera, hasRevokedLicense(true)); 
} 
/*failure output: java.lang.AssertionError:
Expected: Revoking license should have returned <true>
but: returned <false>*/
public void shouldNotRevokeLicense() { 
vehicle.passSpeedCamera(104);
assertThat(speedCamera, hasRevokedLicense(false)); 
} 
/*failure output: java.lang.AssertionError: 
Expected: Revoking license should have returned <false>
but: returned <true>*/ 
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public void shouldCorrectSpeedToZero() { 
vehicle.passSpeedCamera(0); 
assertThat(speedCamera, hasCorrectedSpeedTo(0)); 
} 
/*failure output: java.lang.AssertionError: 
  Expected: The camera should have corrected the speed to <0> 
  but: alas, the camera corrected it to <-3>*/
public void shouldCorrectSpeedToHundred() { 
vehicle.passSpeedCamera(105); 
assertThat(speedCamera, hasCorrectedSpeedTo(100)); 
} 
/*failure output: java.lang.AssertionError: 
  Expected: The camera should have corrected the speed to <100> 
  but: alas, the camera corrected it to <99>*/
public void shouldTakePicture() { 
vehicle.passSpeedCamera(54); 
assertThat(speedCamera, hasTakenAPicture(true)); 
} 
/*failure output: java.lang.AssertionError:
  Expected: Taking a picture should have returned <true> 
  but: returned <false>*/
public void shouldNotTakePicture() { 
vehicle.passSpeedCamera(53); 
assertThat(speedCamera, hasTakenAPicture(false)); 
} 
/*failure output: java.lang.AssertionError:
  Expected: Taking a picture should have returned <false> 
  but: returned <true>*/
public void shouldRevokeLicense() { 
vehicle.passSpeedCamera(105); 
assertThat(speedCamera, hasRevokedLicense(true)); 
} 
/*failure output: java.lang.AssertionError:
  Expected: Revoking license should have returned <true>
  but: returned <false>*/
public void shouldNotRevokeLicense() { 
vehicle.passSpeedCamera(104);
assertThat(speedCamera, hasRevokedLicense(false)); 
} 
/*failure output: java.lang.AssertionError: 
  Expected: Revoking license should have returned <false>
  but: returned <true>*/ 

In my next article we will look at a safe way to chain our freshly made custom BaseMatchers.

Retrieve all testcase data with HP QC OTA and Python

During my current assignment I was asked if I could provide the data table parameters I used in all my HP Quick Test Pro test cases. Normally this would mean opening QTP, export the data table to an Excel file and open the next test case. Since I had to do this for more than 200+ test cases and they were all on the HP Quality Centre server (which means the test cases would open even slower than usual) I tried to find a faster solution.

I used Python 3.4 and the Quality Center OTA API to access the data directly on the QC server and download it to a local drive.

First the Python class to connect to QC OTA:

'''
@author: Michel Lalmohamed
'''
from win32com.client import Dispatch
class QC_ConnectorClass(object):
'''
Connector Class which returns as the connection object for further QC API use
'''
def __init__(self):
print("class init")
# This function creates a connection to the HP QC server and logs in to the server by using the
# user credentials
def ConnectToQC(self, qcServer, qcUser, qcPassword, qcDomain, qcProject):
#HP QC OTA methods
self.TD = Dispatch("TDApiOle80.TDConnection.1")
self.TD.InitConnectionEx(qcServer)
self.TD.Login(qcUser,qcPassword)
self.TD.Connect(qcDomain,qcProject)
print("Logged in")
#Return the connection object
return self.TD
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
'''
@author: Michel Lalmohamed
'''
from win32com.client import Dispatch
class QC_ConnectorClass(object):
'''
    Connector Class which returns as the connection object for further QC API use
    '''
def __init__(self):
print("class init")
# This function creates a connection to the HP QC server and logs in to the server by using the
# user credentials
def ConnectToQC(self, qcServer, qcUser, qcPassword, qcDomain, qcProject):
#HP QC OTA methods
self.TD = Dispatch("TDApiOle80.TDConnection.1")
self.TD.InitConnectionEx(qcServer)
self.TD.Login(qcUser,qcPassword)
self.TD.Connect(qcDomain,qcProject)
print("Logged in")
#Return the connection object
return self.TD

This class returns the connection object so we can now use it to retrieve all the testcase data in our script.

'''
Created on 17 feb. 2015
@author: Michel Lalmohamed
'''
import time
from QC_Connector import QC_ConnectorClass
#QC login user credentials
qcServer = "[http://qc11.*******.**/qcbin"]http://qc11.*******.**/qcbin";
qcUser = "milalmohamed"
qcPassword = ********
qcDomain = *******
qcProject = *******
#Connection is being handled by the ConnectorClass & ConnectToQC method.
#Returns the QC connection object
QC_ConnectedObj = QC_ConnectorClass().ConnectToQC(qcServer, qcUser, qcPassword, qcDomain, qcProject)
#Grab the entire folder tree structure from the QC project as an object. This gives the user all the power to search the entire project structure
TreeObject = QC_ConnectedObj.TreeManager
#Specify which folder and sub folders you want to search in but because we grabbed the entire tree a high level folder is already sufficient! 
folder = TreeObject.NodeByPath("Subject\<the main folder which contains all the tests>")
#Name prefix of the tests you want to find. We don’t have to now the entire name of the test. 
#A partial name will already return the correct result. In this situation a test case prefix is enough to get ALL the cases. Just like we want, a complete list with test cases. 
testList = folder.FindTests("<search prefix>")
if (len(testList)) == 0:
print("No Tests found")
else:
#Loop through all the test objects in the list
for test in range(len(testList)):
print (str(testList[test].ID) + "; " + testList[test].name)
#grab all test data from the test including the data sheet en script files
TestStorage = testList[test].ExtendedStorage
#set path to download files 
TestStorage.ClientPath = 'c:\\QC_Extract\\' + testList[test].name
#download all the test case data
TestStorage.Load("", True)
#progress tracker
print(len(testList) - test )
#pause to not overload the server
time.sleep(5)
QC_ConnectedObj.Logout()
print("Logged out")
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
'''
Created on 17 feb. 2015
@author: Michel Lalmohamed
'''
import time
from QC_Connector import QC_ConnectorClass
#QC login user credentials
qcServer = "[http://qc11.*******.**/qcbin"]http://qc11.*******.**/qcbin";
qcUser = "milalmohamed"
qcPassword = ********
qcDomain = *******
qcProject = *******
#Connection is being handled by the ConnectorClass & ConnectToQC method.
#Returns the QC connection object
QC_ConnectedObj = QC_ConnectorClass().ConnectToQC(qcServer, qcUser, qcPassword, qcDomain, qcProject)
#Grab the entire folder tree structure from the QC project as an object. This gives the user all the power to search the entire project structure
TreeObject = QC_ConnectedObj.TreeManager
#Specify which folder and sub folders you want to search in but because we grabbed the entire tree a high level folder is already sufficient! 
folder = TreeObject.NodeByPath("Subject\<the main folder which contains all the tests>")
#Name prefix of the tests you want to find. We dont have to now the entire name of the test. 
#A partial name will already return the correct result. In this situation a test case prefix is enough to get ALL the cases. Just like we want, a complete list with test cases. 
testList = folder.FindTests("<search prefix>")
if (len(testList)) == 0:
print("No Tests found")
else:
#Loop through all the test objects in the list
for test in range(len(testList)):
print (str(testList[test].ID) + "; " + testList[test].name)
#grab all test data from the test including the data sheet en script files
TestStorage = testList[test].ExtendedStorage
#set path to download files 
TestStorage.ClientPath = 'c:\\QC_Extract\\' + testList[test].name
#download all the test case data
TestStorage.Load("", True)
#progress tracker
print(len(testList) - test )
#pause to not overload the server
time.sleep(5)
QC_ConnectedObj.Logout()
print("Logged out")

All the QTP test case data is now stored in the specified folder. Which in my case meant writing a simple script to extract the default.xls, which contained the parameter data, from every folder and rename it.

Please take caution when extracting lots of data from the QC server. Especially the ExtendedStorage can cause the server to crash. Hence my little wait function to keep IT support of my back.

agt_utilities

Snel prototypen met HP QTP. Het kan!

3 belangrijke aspecten van prototypen in QTP

In de snelle wereld van Agile zijn we wellicht geneigd om aan kleinere opensource tools te denken als het om prototypen gaat voor test automatisering. Het moet snel resultaat opleveren, quick and dirty. Een grote tool als HP’s Quick Test Professional wordt dan over het hoofd gezien, maar dit is naar mijns inziens onterecht. Met de juiste kennis en gebruik van de toolset kun je wel degelijk snel prototypen met QTP.

Voor snel prototypen zijn 3 aspecten zeer belangrijk:

  1. Kennis van VBscripten.
  2. De Object Spy
  3. De Object Repository.

Met een redelijke begrip van VBscripten kan in QTP de “expert view” gebruikt worden. Dit geeft de gebruiker de mogelijkheid om in QTP direct de stappen van de test case te scripten in plaats van op te nemen. De Object Spy en Object Repository worden gebruikt om elementen van het test object niet alleen te identificeren maar ook om op te slaan in een databank (inderdaad een repository) voor verder gebruik. Knoppen, tekst velden, radio buttons en plaatjes. Alle webelementen kunnen in dit geval worden weg geschreven naar deze databank. Willen wij dus op een knop drukken, dan is descriptive scripting, elementen benoemen aan de hand hun HTML kenmerken, niet meer nodig maar is een referentie naar de knop in de Object Repostory (OR) al voldoende om de knop in te drukken. Dit scheelt tijd, code tikken en maakt het geheel ook nog eens herbruikbaar voor als de knop onder de motorkap verandert. We hoeven dan de knop alleen in de OR aan te passen, niet in onze code.

OS OR

Nu dan even naar de praktijk. De happy flow van een test website is bekend. Hoe nu te werk?

Identificeer allereerst de elementen die gebruikt worden in de flow. Welke knoppen worden ingedrukt? Welke velden worden ingevuld met data? We doen dit met Object Spy. Klik simpel weg op het object met de spy-mode actief en zie vervolgens de data verschijnen in QTP. Na deze procedure wordt het element weggeschreven naar de OR met een logische (korte) naam. Nadat alle element zijn opgeslagen richten je je op de expert view in QTP en beginnen te scripten:

De happy flow wordt gevolgd van A tot Z. Inlog gegevens invullen? Scripten in QTP:

code1

Moeten we op de inlog knop drukken? Scripten in QTP:

code2

Zoeken naar een product en klikken op zoeken? Scripten in QTP:

code3

Na de hele flow gescript te hebben, save je de file, druk je op de run knop met als conclusie dat het geautomatiseerde prototype af is.

Omdat dit een prototype is zijn er geen parameters gebruikt, de datatable met rust gelaten, geen aparte actions gebruikt en de validatie tussen de stappen niet gescript. Met dit prototype is aangetoond dat de elementen geïdentificeerd konden worden en de website te automatiseren is met QTP. In een agile omgeving kan dit al snel duidelijk verschaffen hoe verder te gaan met automatiseren, sprint planning indelen enz.

Door middel van de datatable functionaliteit in QTP kunnen we dit prototype uitbreiden naar een volwaardige test case waarin meerdere parameters mogelijk zijn. Hier over meer in een volgende QTP blog post.

Waarom faalt FitNesse als testautomatiseringstool?

FitNesse LogoVeel organisaties gebruiken FitNesse als dé testautomatiseringstool. Naar mate het project vordert, vordert ook het aantal tests in FitNesse en daarmee ook de tijd om deze tests te onderhouden. Tot men op een punt komt dat het meer tijd kost om de testset te onderhouden dan een nieuwe functionaliteiten op te leveren en zetten ze FitNesse aan de kant. Men stapt over naar een commerciële tool, of bouwt een compleet nieuw systeem om de tests te automatiseren. Dit heeft als gevolg dat veel werk verloren gaat en in de prullenmand verdwijnt. Hoe komt het dat FitNesse faalt als testautomatiseringstool?

Niet-technische testers

Eén van de belangrijkste redenen om te kiezen voor FitNesse is de veronderstelling, dat door gebruik te maken van FitNesse de tester geen kennis hoeft te hebben van programmeren en geen code hoeven te kunnen lezen. Niets is echter minder waar, want FitNesse is slechts een schil om de code heen en maakt intensief gebruik van de klassen en methodes in de code. Enige basiskennis over hoe code is opgebouwd en hoe FitNesse de vertaling maakt van de commando’s in de pagina’s naar de klassen en methodes in de code is daardoor wel degelijk noodzakelijk.

DRY, ook in testautomatisering

Omdat de tester geen kennis heeft van programmeren, is hij vaak ook niet bekend met het DRY-principe (Don’t-repeat-yourself). Dit heeft tot gevolg dat de stappen van veel voorkomende procedures, zoals inloggen, in iedere afzonderlijke test opnieuw zijn uitgeschreven. Als er dan een kleine wijziging in 1 van die stappen wordt doorgevoerd, moet men die wijziging in alle testen aanpassen.

Logica in testscript

Een andere veel voorkomende fout is het opnemen van logica in de tests. Ondanks dat FitNesse automatisering mogelijk maakt, behoren de tests nog steeds lineair te blijven en niet meer dan 1 mogelijke uitkomst bevatten. Door logica in een test op te nemen, is het onderzoek naar de reden van het falen van de test extra gecompliceerd en tijdrovend.

FitNesse is niet bedoeld voor functionele tests

Bij organisaties die FitNesse gebruiken als automatiseringstool valt op dat, naast acceptatietests, ook de functionele en de regressietests geschreven zijn en worden gebruikt in de buildstraat. Dit is te vergelijken met het bijhouden van de gemeentelijk basisadministratie in een Excel-bestand. FitNesse is gemaakt om de samenwerking tussen business en IT te verbeteren op het gebied van requirements. Daarnaast kan FitNesse gebruikt worden om, op basis van de requirements, acceptatietests te automatiseren.

Conclusie

Het is dus niet FitNesse dat faalt, maar de organisaties falen in het juist implementeren van testautomatisering en het selecteren van de juiste tool voor de taak. Een kanon gebruiken om een mug te doden is overdreven, maar het omgekeerde, een olifant proberen te stoppen met een luchtbuks is onbegonnen werk. Dit laatste is echter wel waarmee de inzet van FitNesse veelal vergeleken moet worden. Daarbij wordt ten onrechte verondersteld dat door tools te gebruiken, de tester geen kennis hoeft te hebben van programmeren en code om goede automatische tests te maken en te onderhouden.

Run your Selenium tests in the cloud with TestingBot

Ideally you want to run your tests in all different browsers on all platforms. It can be achieved with Selenium Grid. Setting up Selenium Grid might be an time-consuming task and you have to deal with license costs. Therefore, some companies providing cloud-based solutions.

TestingBot offers a cloud-based solution with many extra functionalities, such as: taking screenshots per command, video recording of the test execution, integrated proxy and advanced reporting.

TestingBot can be used with all kinds of programming languages. In this tutorial you will see how easy it is to run your tests in the cloud.

How to to it

  1. Create an account on TestingBot and try it for free. (or choose a sufficient plan)
  2. Download the required library from: https://github.com/testingbot
  3. Change the remote address, so the tests will be run at the remote Grid.

import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class TestingBotTest {
private WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
DesiredCapabilities capabillities = DesiredCapabilities.firefox();
capabillities.setCapability("version", "11");
capabillities.setCapability("platform", Platform.WINDOWS);
capabillities.setCapability("name", "Testing Selenium 2");
driver = new RemoteWebDriver(
new URL(
"http://ClientKey:ClientSecret@hub.testingbot.com:4444/wd/hub"),
capabillities);
}
@Test
public void testSimple() throws Exception {
driver.get("http://techblog.polteq.com/prestashop/");
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys(
"ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']"))
.click();
String searchHeader = driver.findElement(By.cssSelector("H1"))
.getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
@AfterClass
public void tearDown() throws Exception {
driver.quit();
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class TestingBotTest {
private WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
DesiredCapabilities capabillities = DesiredCapabilities.firefox();
capabillities.setCapability("version", "11");
capabillities.setCapability("platform", Platform.WINDOWS);
capabillities.setCapability("name", "Testing Selenium 2");
driver = new RemoteWebDriver(
new URL(
"http://ClientKey:ClientSecret@hub.testingbot.com:4444/wd/hub"),
capabillities);
}
@Test
public void testSimple() throws Exception {
driver.get("http://techblog.polteq.com/prestashop/");
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys(
"ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']"))
.click();
String searchHeader = driver.findElement(By.cssSelector("H1"))
.getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
@AfterClass
public void tearDown() throws Exception {
driver.quit();
}
}

Additional Test Options

Screenshots

Capture screenshots at every step of the test.


capabillities.setCapability("screenshot", true);
1
capabillities.setCapability("screenshot", true);

Video

Record a video of your test, which is accessible in the member area.


capabillities.setCapability("screenrecorder", true);
1
capabillities.setCapability("screenrecorder", true);

Test Name

Add a name to this test, which will show up in our member area.


capabillities.setCapability("name", "Test Script Name");
1
capabillities.setCapability("name", "Test Script Name");

Custom data

Send along custom data, for example your build number, release, server, commit hash, etc…


capabillities.setCapability("extra", "release 1.2.3");
1
capabillities.setCapability("extra", "release 1.2.3");

Launch Selenium node with a JSON configuration file

Problem

We can set parameters to the node in order to configure them. We can set for example the maximum number of concurrent tests, platform, browserName and so on. We can use those values in our tests to specify the corresponding node.

Prerequisites

Open the command prompt and navigate to the directory where we put the selenium-server-standalone file.

Solution

We can add the -browser parameters to the command line with the following values to launch 5 FireFox 3.6 instances on Linux.

-browser browserName=firefox,version=3.6,maxInstances=5,platform=LINUX

There is more…

We can also load a JSON file while launching the node, instead of specifying the browser parameter on the command line. We need to add the following parameter to the test: -nodeConfig node.json The contents of node.json may look like this:


{
"capabilities":
[
{
"browserName": "firefox",
"version": "3.6",
"platform": "WINDOWS",
"maxInstances": 1
},
{
"browserName": "internet explorer",
"version": "8",
"platform": "WINDOWS",
"maxInstances": 1
}
],
"configuration":
{
"nodeTimeout":120,
"port":5555,
"hubPort":4444,
"hubHost":"localhost",
"nodePolling":2000,
"registerCycle":10000,
"register":true,
"cleanUpCycle":2000,
"timeout":30000,
"maxSession":1,
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"capabilities":
[
{
"browserName": "firefox",
"version": "3.6",
"platform": "WINDOWS",
"maxInstances": 1
},
{
"browserName": "internet explorer",
"version": "8",
"platform": "WINDOWS",
"maxInstances": 1
}
],
"configuration":
{
"nodeTimeout":120,
"port":5555,
"hubPort":4444,
"hubHost":"localhost",
"nodePolling":2000,
"registerCycle":10000,
"register":true,
"cleanUpCycle":2000,
"timeout":30000,
"maxSession":1,
}
}

It makes it possible to use a firefox and Internet Explorer browser on a windows platform.

Command line script to start a Selenium Node with JSON configuration file:

java -jar selenium-server-standalone-<version>.jar -role node -hub http://localhost:4444/grid/register -nodeConfig defaultNodeConfig.json

Run test scripts against Selenium Grid

Problem

In order to run test scripts against Selenium Grid, we have to make minor changes. This recipes will show us how we can define capabilities to match a node.

Prerequisites

Make sure the desired capabilities are in the Grid otherwise the test can not be routed to the node.

Solution

The code below will match the following nodes: browserName=firefox,version=3.6,platform=LINUX


import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GridExample {
private static RemoteWebDriver driver;
@BeforeClass
public void setUp() throws MalformedURLException {
DesiredCapabilities capability = DesiredCapabilities.firefox();
capability.setBrowserName("firefox");
capability.setPlatform(Platform.LINUX);
capability.setVersion("3.6");
driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capability);
driver.get("http://techblog.polteq.com/prestashop/");
}
@Test
public void measurePerformance() {
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GridExample {
private static RemoteWebDriver driver;
@BeforeClass
public void setUp() throws MalformedURLException {
DesiredCapabilities capability = DesiredCapabilities.firefox();
capability.setBrowserName("firefox");
capability.setPlatform(Platform.LINUX);
capability.setVersion("3.6");
driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), capability);
driver.get("http://techblog.polteq.com/prestashop/");
}
@Test
public void measurePerformance() {
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}

Add nodes to the Selenium Grid Hub

Problem

We can add nodes to the Selenium Grid Hub, now the hub is up and running. This recipe will describe how to launch Selenium instances and register them to the hub so it starts forming the grid op Selenium instances that we are expecting.

Prerequisites

Open the command prompt and navigate to the directory where we put the selenium-server-standalone file.

Solution

Enter something like the following command in the command prompt: java -jar selenium-server-standalone-.jar -role node -hub http://localhost:4444/grid/register -port 5556 We should see the following output in the console:

What has been done

The command above will register a node to the hub, which is fully backwards compatible with Selenium 1. We can see this node in the Hub Console:

We can add multiple operating systems to our Selenium Grid. This allows us to check, whether different versions of Firefox on different operating systems are showing the same behavior.

Launching the Selenium Grid Hub

Problem

We want to start Selenium HUB, which is the central point in the Grid that will receive all Selenium commands and route them to the right node.

Prerequisites

Download the latest version of selenium-server-standalone.jar from the Selenium website. (http://code.google.com/p/selenium/downloads/list) Open the command prompt and navigate to the directory where we put the selenium-server-standalone file.

Solution

Enter the following command in the command prompt to start the hub: java -jar selenium-server-standalone-.jar -role hub The output in the console will look like this:

What has been done

The hub will use port 4444 by default. We can see the Selenium instances that are connected to the hub and the status of the nodes if we put http://ip-of-hub:4444/grid/console in a browser window, where ip-of-hub is the name of the machine with the hub. If it is on the local machine then you can place http://localhost:4444/grid/console. We can see that in the next screenshot:

Tip: We can also specify a different port to run the hub on. We can give an extra parameter to the command, like: -port # (where # is the port-number)

Selenium Grid introduction

Selenium Grid allows us to have multiple Selenium instances on multiple machines and then have one point to send Selenium commands to. We can specify the operating system / browser and browser version where we want to run the test on. The hub which is the central point of the Grid will route all the Selenium command to the requested node.

In this section of the blog we will see how to set up Selenium Grid and how to make use of some common properties.

Running tests on an Android simulator

Problem

The android driver allows us to execute our tests against an Android browser. This can be a simulator or a real device. This recipe will walk us through the steps we have to do to run our tests in a simulator.

Prerequisites

Before we can register our simulator we have to download the android SDK (Software Development Kit) from the following location: http://developer.android.com/sdk/

Solution

We can divide this section into three parts: setup the emulator, install the WebDriver APK and finally run the test.

Setup the emulator

Navigate to the tools directory and create an Android Virtual Device.


$cd ~/android_sdk/tools/
$./android create avd -n my_android -t 12 -c 100M
1
2
$cd ~/android_sdk/tools/
$./android create avd -n my_android -t 12 -c 100M

-n: specifies the name of the AVD -t: specifies the platform target -c: specifies the SD card storage space

We can list the targets with the following command to check if the creation succeeded:

./android list targets

Finally we can start the emulator with the following command:

$./emulator -avd my_android &

Install the WebDriver APK

  1. We need to retrieve the serial id with the following command: /android_sdk/platform-tools/adb devices
  2. Download the Android server from http://code.google.com/p/selenium/downloads/list and save it in the platform-tools directory. To install the application enter: $./adb -s <serialId> -e install -r android-server.apk
  3. Start the Android WebDriver application,by running this command: $./adb -s <serialId> shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity
  4. Now we need to setup the port forwarding in order to forward traffic from the host machine to the emulator. Enter the following in the terminal: $./adb -s <serialId> forward tcp:8080 tcp:8080

Run the test

Now we have our environment setup we can run our tests. The test will look like this:


import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.android.AndroidDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileAndroidDevice {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new AndroidDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.android.AndroidDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileAndroidDevice {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new AndroidDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}

Run tests on a real Android device

Problem

We want to run our testscripts on a real Android device. The android driver allows us to execute our tests against an Android browser. This can be a simulator or a real device.

Prerequisites

Before we can register our device we have to download the android SDK (Software Development Kit) from the following location: http://developer.android.com/sdk/

Solution

We can divide this section into three parts: setup the device, install the WebDriver APK and finally run the test.

Setup the device

Connect the android device with the computer using a USB cable.

Install the WebDriver APK

  1. We need to retrieve the serial id with the following command: /android_sdk/platform-tools/adb devices
  2. Download the Android server from http://code.google.com/p/selenium/downloads/list and save it in the platform-tools directory. To install the application enter: $./adb -s <serialId> -e install -r android-server.apk
  3. Start the Android WebDriver application,by running this command: $./adb -s <serialId> shell am start -a android.intent.action.MAIN -n org.openqa.selenium.android.app/.MainActivity
  4. Now we need to setup the port forwarding in order to forward traffic from the host machine to the emulator. Enter the following in the terminal: $./adb -s <serialId> forward tcp:8080 tcp:8080

Run the test

Now we have our environment setup we can run our tests. The test will look like this:


import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.android.AndroidDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileAndroidDevice {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new AndroidDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.android.AndroidDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileAndroidDevice {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new AndroidDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}

Run test scripts against a real iPhone device

Problem

We want to execute our testscripts on a iphone device. The iphone driver allows us to execute the WebDriver tests on a real iphone device.

Prerequisites

We need to install Xcode which is available for the Mac operating systems. We also need a provisoning profile for running the script on a real device.

  1. We need to create a provisioning profile, so go to this website http://developer.apple.com/programs/ios/ and create a profile.
  2. We need to download Xcode from the following location: http://developer.apple.com/iphone
  3. The iPhone WebDriver application is not available from the App Store, therefore we need to check-out the code and build it manually. We can do the check-out by entering the following terminal command: svn checkout http://selenium.googlecode.com/svn/trunk/ selenium-read-only

More information on SVN checkouts: http://www.linuxfromscratch.org/blfs/edguide/chapter03.html

Solution

  1. Open the project selenium-read-only/iphone/iWebDriver.xcodeproj in Xcode.
  2. Double-click on the project and set the build configuration to the latest version of the iPhone Simulator, by using the drop-down box in the top left corner.
  3. Configure and install the iPhone provisioning profile.
  4. Open Info.plist and edit the Bundle Identifier to com.NAME.$ {PRODUCT_NAME:identifier} where NAME is the name you registered your provisioning profile to be an authority on.
  5. Make sure your device is connected to your computer. Your device must also be routable from your computer. The easiest way to do this is to configure a wifi network and connect your device to it.
  6. Click on Run (play button). After compiling, the iphone simulator should appear and the iWebDriver app will be installed in it.
  7. Now we can use the IPhoneDriver in our tests.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.iphone.IPhoneDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileIphoneSimulator {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new IPhoneDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.iphone.IPhoneDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileIphoneSimulator {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new IPhoneDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}

Run test scripts on a Xcode iPhone simulator

Problem

We want to run our test in a iPhone simulator. The iphone simulator comes with Xcode which is available for the Mac operating systems. This recipe will show us how we can run our WebDriver tests in a iphone simulator.

Prerequisites

We need to download Xcode from the following location: http://developer.apple.com/iphone

The iPhone WebDriver application is not available from the App Store, therefore we need to check-out the code and build it manually. We can do the check-out by entering the following terminal command: svn checkout http://selenium.googlecode.com/svn/trunk/ selenium-read-only

More information on SVN checkouts: http://www.linuxfromscratch.org/blfs/edguide/chapter03.html

Solution

  1. Open the project selenium-read-only/iphone/iWebDriver.xcodeproj in Xcode.
  2. Double-click on the project and set the build configuration to the latest version of the iPhone Simulator, by using the drop-down box in the top left corner.
  3. Click on Run (play button). After compiling, the iphone simulator should appear and the iWebDriver app will be installed in it.
  4. Now we can use the IPhoneDriver in our tests.

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.iphone.IPhoneDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileIphoneSimulator {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new IPhoneDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.iphone.IPhoneDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class MobileIphoneSimulator {
private static WebDriver driver;
@BeforeClass
public void setUp() throws Exception {
driver = new IPhoneDriver();
driver.get("http://techblog.polteq.com/prestashop/");
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void measurePerformance() throws InterruptedException {
Thread.sleep(1500);
driver.findElement(By.cssSelector("input#search_query_top")).sendKeys("ipod nano");
driver.findElement(By.cssSelector("input[name='submit_search']")).click();
String searchHeader = driver.findElement(By.cssSelector("H1")).getText().toLowerCase();
Assert.assertTrue(searchHeader.contains("ipod nano"));
}
}

Perform a sequence of actions with Selenium WebDriver

Problem

We want to perform multiple actions in once, like: drag-and-drop, sliding, selecting multiple items.

Solution

The example code below shows some examples where we can use the Actions interface of Selenium WebDriver.


import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class ActionExample {
private static WebDriver driver;
@BeforeClass
public void setUp() {
driver = new FirefoxDriver();
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void draggable() {
driver.get("http://jqueryui.com/demos/draggable/");
WebElement draggable = driver.findElement(By.id("draggable"));
new Actions(driver).dragAndDropBy(draggable, 120, 120).build()
.perform();
}
@Test
public void droppable() {
driver.get("http://jqueryui.com/demos/droppable/");
WebElement draggable = driver.findElement(By.id("draggable"));
WebElement droppable = driver.findElement(By.id("droppable"));
new Actions(driver).dragAndDrop(draggable, droppable).build().perform();
}
@Test
public void selectMultiple() throws InterruptedException {
driver.get("http://jqueryui.com/demos/selectable/");
List<WebElement> listItems = driver.findElements(By
.cssSelector("ol#selectable *"));
Actions builder = new Actions(driver);
builder.clickAndHold(listItems.get(1)).clickAndHold(listItems.get(2))
.click();
Action selectMultiple = builder.build();
selectMultiple.perform();
}
@Test
public void sliding() {
driver.get("http://jqueryui.com/demos/slider/");
WebElement draggable = driver.findElement(By
.className("ui-slider-handle"));
new Actions(driver).dragAndDropBy(draggable, 120, 0).build().perform();
}
}
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import java.util.List;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class ActionExample {
private static WebDriver driver;
@BeforeClass
public void setUp() {
driver = new FirefoxDriver();
}
@AfterClass
public void tearDown() {
driver.close();
driver.quit();
}
@Test
public void draggable() {
driver.get("http://jqueryui.com/demos/draggable/");
WebElement draggable = driver.findElement(By.id("draggable"));
new Actions(driver).dragAndDropBy(draggable, 120, 120).build()
.perform();
}
@Test
public void droppable() {
driver.get("http://jqueryui.com/demos/droppable/");
WebElement draggable = driver.findElement(By.id("draggable"));
WebElement droppable = driver.findElement(By.id("droppable"));
new Actions(driver).dragAndDrop(draggable, droppable).build().perform();
}
@Test
public void selectMultiple() throws InterruptedException {
driver.get("http://jqueryui.com/demos/selectable/");
List<WebElement> listItems = driver.findElements(By
.cssSelector("ol#selectable *"));
Actions builder = new Actions(driver);
builder.clickAndHold(listItems.get(1)).clickAndHold(listItems.get(2))
.click();
Action selectMultiple = builder.build();
selectMultiple.perform();
}
@Test
public void sliding() {
driver.get("http://jqueryui.com/demos/slider/");
WebElement draggable = driver.findElement(By
.className("ui-slider-handle"));
new Actions(driver).dragAndDropBy(draggable, 120, 0).build().perform();
}
}