Improving Your Selenium WebDriver Tests - Belgium testing days_2016
1. Improving Your Selenium
WebDriver Tests
Roy de Kleijn
Technical Test Consultant
Email: roy.dekleijn@the-future-group.com
Twitter: @TheWebTester
Website: http://www.rdekleijn.nl
Github: https://github.com/roydekleijn
3. Answer #1
Depending on third-party data
Synchronization issues
Cross-browser issues
Hard to locate elements
testdata
Slow feedback cycle
Flaky tests
High maintenance costs
5. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
7. Testing Pyramid
unit
UI
API
feedback-cycle
- Extremely fast
- Smallest units of the application / isolates failure
- Executed during build time
- No dependency on data
- Extremely slow
- Requires running application
- Will change frequently
- Dependency on data
- Fast
- Covering boundary conditions
- Start early in SD process
- Requires running application
- (some) dependency on data
9. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
10. Question #2
What is wrong with these locators?
#1
.//*[@id='wx-header-wrap']/div/div/div/div[2]/div[2]/div/section/div/form/input
#2
.//*[@id='gnav-header-inner']/div/ul/li[2]/a
17. AngularJS - elements
• Different way of locating elements
• Binding
• Model
• Repeat
• ngWebDriver library (create by Paul Hammant)
• https://github.com/paul-hammant/ngWebDriver
• Logic from Protractor project
18. • Enable debug info
• Call angular.reloadWithDebugInfo(); in your browser debug console
• Execute the following snippet to reveal all the elements:
var bindings = document.getElementsByClassName('ng-binding');
for (var i = 0; i < bindings.length; ++i) {
var bindingName = angular.element(bindings[i]).data().$binding[0].exp
||angular.element(bindings[i]).data().$binding;
console.log(bindingName.toString());
console.log(bindings[i]);
}
19. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
20. Problems that arise
• Unmaintainable
• Unreadable test scripts
• Creation of test scripts is time consuming
• Code duplication
22. Solution
Each page contains only a part of the total functionality available on
the website
Put page specific functionality in a class with a corresponding name
23. Step-by-step plan
1. Identify necessary WebElements
2. Create a class
3. Define WebElements in corresponding classes
4. Model the functionality of a page into methods
5. Model the page flow by setting returntypes
25. Create a class
A class with the name of the page extending from
LoadableComponent
public class HomePage extends LoadableComponent<HomePage> {
private WebDriver driver;
public HomePage(WebDriver driver) {
this.driver = driver;
PageFactory.initElements(driver, this);
}
26. Define WebElements
On class level (above the methods)
@FindBy(css = "a.login")
private WebElement loginLink;
28. Model the page flow
• Prerequisite:
• Multiple pages are modelled
• Modify returntype
• The returntype is the name of the page (class) where you are navigating
towards
• Use the current class name, if you stay on the same page
29. Model the page flow
public LoginPage clickOnLoginLink() {
loginLink.click();
return new LoginPage(driver);
}
Returning page
30. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
31. Data Objects
final CustomerAccount account = new
CustomerAccount.CustomerAccountBuilder("test@test.com","1qazxsw2").build();
Access data:
account.getEmail()
account.getPassword()
32. Data Objects - Complex
final Order order = new Order.OrderBuilder()//
.withInvoiceAddress(new Address.AddressBuilder("abc street", "1234ab").build())//
.withShippingAddress(new Address.AddressBuilder("shipstreet”,
"4321ab").withCountry("The Netherlands").build())//
.build();
// Retrieve data from the object
System.out.println(order.getInvoiceAddress().getStreet());
System.out.println(order.getShippingAddress().getCountry());
33. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
34. Synchronization issues
• Browser has to start
• Page has to load
• AJAX request need to be finished
• Or, loader should be gone before we can continue
35. What NOT to do …
NEVER, use Thread.sleep();
• It will probably make your test unnecessary slow
• You never know if you wait exactly long enough
36. What to do…
• WebDriver build in wait mechanisms
• implicitlyWait: poll till element is present
• setScriptTimeout: time to wait for an asynchronous script to finish
• pageLoadTimeout: time to wait for a page load to complete
• ngWebDriver
• waitForAngularRequestsToFinish – wait for outstanding angular requests
• Custom (gist)
• checkPendingRequests – wait for all HTTP requests to be finished
37. Example 1
Wait for element to be clickable
@Test
public void waitForElementToBeClickable() {
new WebDriverWait(driver, 20,100) //
.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("a.login")));
}
38. Example 2
Wait for loader to be invisible
@Test
public void waitForElementNotToBeVisable() {
new WebDriverWait(driver, 20, 100) //
.until(ExpectedConditions.invisibilityOfElementLocated(
By.cssSelector("loader")));
}
39. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
40. Speed-up and stabilize your tests
Windows 7
IE
FF
Chrome
Windows vista
IE
FF
Ubuntu
FF
Opera
Mac OS
FF
Chrome
Opera
…
Nodes
Hub
Specification
HUB
Test Scripts
41. Docker Selenium
• Disposable Selenium Grid (in seconds)
• Autoscaling features
• https://hub.docker.com/r/selenium/
43. Docker commands
docker-compose up –d
-d: Run containers in the background
--force-recreate: Recreate containers entirely
Autoscaling:
docker-compose scale firefoxnode=5 chromenode=1
44. In practice
Implement or extend the Page Object Model
• Website
• url: http://demo.technisch-testen.nl
• Source
• Github: https://github.com/roydekleijn/webdriver-workshop
45. Contents
• Introduction
• Element locator tips & tricks
• Implementing the Page Object Model
• Utilize Data Objects
• Handle synchronization
• Speed-up and stabilize your tests (demo)
• What we have learned
We will start with an actual Page Object Model implementation today
46. What we have learned today
Depending on third-party data
Cross-browser issues
Hard to locate elements
testdata
Slow feedback cycle
Flaky tests
High maintenance costs
Synchronization issues
Avoid Thread.sleep()
or other hardcoded
waits
Utilize Page Object
Model
id > name > css >
xpath > angular
Mock interfaces
Run tests in parallel
Mock interfaces
Mock interfaces, setup
clean environments,
implement page object
model
47. Thank you…
Roy de Kleijn
Technical Test Consultant
Email: roy.dekleijn@the-future-group.com
Twitter: @TheWebTester
Website: http://www.rdekleijn.nl
Github: https://github.com/roydekleijn
Notes de l'éditeur
Selenium WebDriver is a popular tool for driving the browsers for test automation. Many companies with browser-based applications have taken steps towards including Selenium WebDriver in their repertoire. There’s also a lot of reports on challenges: brittleness of tests with tests passing and failing randomly, trouble with maintenance as the test suites get bigger and difficulties working with Angular or Ajax enriched applications. If you can’t trust your tests, maintaining them takes a lot of time or your application feels to disagree with being easy to test, your tests might not be adding value.
In this workshop, we focus on Selenium WebDriver tests as code, and look into practices to overcome these common problems. We start with existing tests on an open source application, and we change them through refactoring to improve them. Join this session to improve your ability to create more maintainable and valuable tests with Selenium WebDriver.
Let me start with a question..
In this workshop we are going to cover some of these topics
- Some of these answers are highly related to each other and can be resolved in one go..
Some time ago I have drawn a graph of how a project evolves over time.
Tests bevatten veel driver specieke syntax, waardoor je onleesbare scripts krijgt.
Het maken van scripts duurt vrij lang en wordt als lastig ervaren
Hoe kom je nu tot deze oplossing. Ik heb een 4 stappenplan bedacht om tot een werkbare abstractie te komen. In sommige gevallen kan het wenselijk zijn om voor een hoger abstractie niveau te kiezen. (als bijvoorbeeld veel functionaliteit op de website hetzelfde is en de pagina’s erg op elkaar lijken). Je kan dan “parent classes” maken en deze laten erfen.
OF
Maken van veel voorkomende componenten en deze gebruiken op de specifieke pagina’s.
Voorbeeld van een loginscript van Wehkamp.nl
Zoals we eerder hebben gezien kunnen we webelementen benaderen aan de hand van een locator. Het is aan te raden om deze 1x boven in de class te definieren.
De logische naam (tweede regel) kun je dan gebruiken in alle onderliggende methodes.
Dit bevorderd de onderhoudbaarheid, omdat je de naam van de locator maar op 1 plek hoeft aan te passen bij een wijziging.
Het idee is om methoded te maken die de functionaliteit van de pagine representeren.
Er kan voor gekozen worden om meerdere acties te groeperen in 1 functie. Dit hangt samen met het soort test cases dat gemaakt gaat worden.
Zie vorige sheet.
Why we do this ??
To speed up testing, to be able to run more tests in parallel
We need to do this carefully, because data can change the state of the application and will influence other tests
Selenium WebDriver is a popular tool for driving the browsers for test automation. Many companies with browser-based applications have taken steps towards including Selenium WebDriver in their repertoire. There’s also a lot of reports on challenges: brittleness of tests with tests passing and failing randomly, trouble with maintenance as the test suites get bigger and difficulties working with Angular or Ajax enriched applications. If you can’t trust your tests, maintaining them takes a lot of time or your application feels to disagree with being easy to test, your tests might not be adding value.
In this workshop, we focus on Selenium WebDriver tests as code, and look into practices to overcome these common problems. We start with existing tests on an open source application, and we change them through refactoring to improve them. Join this session to improve your ability to create more maintainable and valuable tests with Selenium WebDriver.