Upload
roy-de-kleijn
View
202
Download
0
Embed Size (px)
Citation preview
Improving Your Selenium WebDriver
Tests
Roy de KleijnTechnical Test ConsultantEmail: [email protected]: @TheWebTesterWebsite: http://www.rdekleijn.nlGithub: https://github.com/roydekleijn
Question #1
What makes your Selenium WebDriver tests suck?
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
Lack of confidence
Results in …
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
Maintenance testM
aint
enan
ce e
ffort
Time
Start
with re
cord an
d playbac
k
Convert
recorded te
sts in
to code
Awesome, it
works
! Let’s
create
more UI te
sts Reality: code ends up into unmaintainable spaghetti
Design patt
erns applie
d
Implement fe
atures t
o mak
e tests
robust
wish
Testing Pyramid
unit
UI
API
feed
back
-cyc
le
- 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
Mock External Interfaces
application
Interface 1
Interface 2
Interface 3
application
Interface 1
Interface 2
Interface 3m
ock
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
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
Answer #2
They contain too much information about the location
Closer look #1
Closer look #1.//*[@id='wx-header-wrap']/div/div/div/div[2]/div[2]/div/section/div/form/input
What if the location of this element will change over time?
It can be written like this: input[class = ‘input--search’]Orinput.input—searchOrinput[name = ‘search’]
Closer look #2
Closer look #2.//*[@id='gnav-header-inner']/div/ul/li[2]/a
What if the order of the links will change over time ?
It can be written like this: a[id=register]Ora#register
Attribute selectorscss xpath
Equals e[a=v] //e[@a=v]
Contains e[a*=v] //e[contains(@a, ‘v’)]
Starts-with e[a^=v] //e[starts-with(@a, ‘v’)]
Ends-with e[a$=v] //e[ends-with(@a, ‘v’)]
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
• 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]);}
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
Problems that arise
• Unmaintainable• Unreadable test scripts• Creation of test scripts is time consuming• Code duplication
From problem to solution
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
Step-by-step plan
1. Identify necessary WebElements2. Create a class3. Define WebElements in corresponding classes4. Model the functionality of a page into methods5. Model the page flow by setting returntypes
Identify necessary WebElements
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); }
Define WebElementsOn class level (above the methods)
@FindBy(css = "a.login")
private WebElement loginLink;
Model the functionality
public LoginPage clickOnLoginLink() { loginLink.click(); return new LoginPage(driver); }
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
Model the page flow
public LoginPage clickOnLoginLink() { loginLink.click(); return new LoginPage(driver); }
Returning page
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
Data Objects final CustomerAccount account = new CustomerAccount.CustomerAccountBuilder("[email protected]","1qazxsw2").build();
Access data:account.getEmail()
account.getPassword()
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 objectSystem.out.println(order.getInvoiceAddress().getStreet());
System.out.println(order.getShippingAddress().getCountry());
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
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
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
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
Example 1
Wait for element to be clickable
@Testpublic void waitForElementToBeClickable() {
new WebDriverWait(driver, 20,100) //.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("a.login")));}
Example 2
Wait for loader to be invisible
@Testpublic void waitForElementNotToBeVisable() {
new WebDriverWait(driver, 20, 100) //.until(ExpectedConditions.invisibilityOfElementLocated(
By.cssSelector("loader")));}
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
Speed-up and stabilize your tests Windows 7
IEFF
Chrome
Windows vistaIEFF
UbuntuFF
Opera
Mac OSFF
ChromeOpera
…
Nodes
Hub
Specification
HUB
Test Scripts
Docker Selenium
• Disposable Selenium Grid (in seconds)• Autoscaling features
• https://hub.docker.com/r/selenium/
Docker-composeseleniumhub: image: selenium/hub ports: - 4444:4444firefoxnode: image: selenium/node-firefox environment: SCREEN_WIDTH: 2880 SCREEN_HEIGHT: 1800 ports: - 5900 links: - seleniumhub:hub
chromenode: image: selenium/node-chrome environment: SCREEN_WIDTH: 2880 SCREEN_HEIGHT: 1800 ports: - 5900 links: - seleniumhub:hub
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
In practice
Implement or extend the Page Object Model
• Website• url: http://demo.technisch-testen.nl
• Source• Github: https://github.com/roydekleijn/webdriver-workshop
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
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 issuesAvoid 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
Thank you…
Roy de KleijnTechnical Test ConsultantEmail: [email protected]: @TheWebTesterWebsite: http://www.rdekleijn.nlGithub: https://github.com/roydekleijn