Upload
jorge-hidalgo
View
100
Download
1
Embed Size (px)
Citation preview
Testing Java Web Applications with Selenium: A Cookbook
JavaOne 2016 – CON3080Jorge Hidalgo & Vicente González
Copyright © 2016 Accenture All rights reserved.
Presenter IntroductionsShameless Marketing Division
2
Copyright © 2016 Accenture All rights reserved.
Presenter Introductions
3
Jorge Hidalgo @_deors deors
Senior Technology ArchitectGlobal Java Community LeadAccenture Spain
Father of two children, husband, irish flute & whistle player, video gamer, master chief apprentice, sci-fi ‘junkie’, Star Wars fan, eternal Lego journeyman, gadget lover and in love with Raspberry Pi computers
Vicente González @viarellano viarellano
Technology ArchitectGlobal Java Community ChampionAccenture Spain
52 month experience husband, proud father of 2 years old princess Regina, senior beach user, cooking lover, associate padel player, master Feria de Abril enthusiast, Sevilla FC fan and Pearl Jam ninja evangelizer
Copyright © 2016 Accenture All rights reserved.
SeleniumWhat is Selenium?
4
Copyright © 2016 Accenture All rights reserved.
Selenium• Selenium is a web browser automation tool• Automate any kind of interaction with a web application• Script to create 1,000 users from data in a spreadsheet• Script to download all invoices in a monthly basis• Automate functional test scripts!
• No backdoor tricks, as close to the real stuff as possible• Excellent for functional test script automation
5
Copyright © 2016 Accenture All rights reserved.
Selenium• Selenium is open source• No vendor lock-in• Smaller inversion (in both capital and operational expenses)
• Selenium is mature and with a large and vibrant community• Runs in many browsers and systems• Scripts can be written in many programming languages• Selenium usage is widely extended• Easier to find people with the skill
6
Copyright © 2016 Accenture All rights reserved.
Selenium• Selenium is formed of various components/projects:• Selenium WebDriver
• Core API and language bindings – to interact with browsers• Browser-specific implementations – because they are not created equal• Executes scripts (tests) in local and remote machines
• Selenium Grid• Simple, efficient, client-server setup to distribute test execution and workload• Across many machines, multiple operating systems and browsers• Even mobile OS/browsers
• Selenium IDE• Firefox plug-in to record and playback tests
7
Copyright © 2016 Accenture All rights reserved.
Demo #1• Selenium in one minute... maybe in two
8
Copyright © 2016 Accenture All rights reserved.
Selenium 101• Writing a test script is simple using the WebDriver API• Very much resembles what the tester is doing
• Scripts are usually wrapped in a test framework like JUnit• Assertion capabilities• Reporting capabilities
WebDriver driver = new ChromeDriver();WebElement findOwnerLink = driver.findElement(By.linkText("Find owner"));findOwnerLink.click();driver.findElement(By.id("lastName")).sendKeys("Schroeder");driver.findElement(By.id("findowners")).click();
9
Copyright © 2016 Accenture All rights reserved.
Selenium 101• The WebDriver API has methods to:• Find elements in a page:
• By its ID (the preferred way)• By the text in a link, CSS selector, class name, element name
• Interact with the elements:• Click a button• Enter text in (or clear) a text field• Select a value in a radio-button or drop-down list
• Access to the page source:• For example, to verify if some JavaScript or some arbitrary text is present
• Take screenshots!
10
Copyright © 2016 Accenture All rights reserved.
Selenium 101• It is even possible to work with DOM using XPath expressions
11
Copyright © 2016 Accenture All rights reserved.
Tip #1Working with the DOM in Selenium
12
Copyright © 2016 Accenture All rights reserved.
Tip #1 – Working with the DOM• The first rule of working with the DOM in Selenium is...
13
Copyright © 2016 Accenture All rights reserved.
Tip #1 – Working with the DOM• The first rule of working with the DOM in Selenium is...
• ...WE DON’T WORK WITH THE DOM IN SELENIUM!
14
Copyright © 2016 Accenture All rights reserved.
Tip #1 – Working with the DOM• We are *very* serious• Working directly with the DOM makes tests brittle• They focus on the implementation detail• They move far from the perspective of the tester
• Interact with visual, recognizable, elements in the UI• Test cases are hard to maintain and easy to break
• Use this option only as a last resort mechanism• And when doing it, document the tests so it is easier to understand
what it pretends
15
Copyright © 2016 Accenture All rights reserved.
Tip #1 – Working with the DOM• As a corollary, two derived tips – these two come from conversations
with developers arguing about ‘why we don’t automate our tests’• Tip #1.1• “Our web pages don’t have IDs”• Always add meaningful IDs in generated / crafted HTML elements• If possible, make them unique (or at least grouped by function)
• Tip #1.2• “The framework we are using generate random or non-consistent IDs”• Pick another framework! • Testability must be a primary driver while taking technology decisions
16
Copyright © 2016 Accenture All rights reserved.
Running Selenium• Step 1)• Start the Selenium grid
• Step 2)• Start the application to be tested
• Step 3)• Launch the tests
17
Copyright © 2016 Accenture All rights reserved.
Running Selenium• Step 1)• Start the Selenium grid
• Add the Selenium folder to PATH – external drivers need to be found at runtime
• Add the Selenium folder to PATH – external drivers need to be found at runtime
• Then each of the test nodes
set PATH=%PATH%;%SELENIUM_HOME% or export PATH=$PATH:$SELENIUM_HOME
java -jar selenium-server-standalone-2.53.1.jar -role node -port 5555 \ -hub http://localhost:4444/grid/register \ -browser browserName=chrome \ -browser browserName=firefox
java -jar selenium-server-standalone-2.53.1.jar -role hub -port 4444
18
Copyright © 2016 Accenture All rights reserved.
Running Selenium• Step 2)• Start the application to be tested
• In your local IDE• A local or remote server (command-line, script, service...)• In a continuous integration job
• Step 3)• Launch the tests
• In your local IDE• A local or remote process (command-line, script...)• In a continuous integration job
19
Copyright © 2016 Accenture All rights reserved.
Demo #1 revisited• Let’s repeat the demo following the process step by step
20
Copyright © 2016 Accenture All rights reserved.
Tip #2Automate Test Data Creation
21
Copyright © 2016 Accenture All rights reserved.
Tip #2 – Test Data Creation• Start by crafting scripts to create test data• Very simple scripts• Use input files (CSV, spreadsheet) to provide data for multiple records• Even if nothing else is automated, there is business value in those
scripts• These scripts are very easy to transform in a functional test script• Add some assertions on the output (ok/error)• Use DbUnit to assert the state in the database is the expected one
22
Copyright © 2016 Accenture All rights reserved.
Tip #2 – Test Data Creation• Using Pet Clinic as a example, let’s create a few owners. Start by
reading the file and feed the test data to the scriptprivate Optional<Owner> parseOwner(String testDataLine) {
String[] testDataTokens = testDataLine.split("\t"); if (testDataTokens.length != 5) { return Optional.empty(); } else { Owner owner = new Owner(); owner.setFirstName(testDataTokens[0]); owner.setLastName(testDataTokens[1]); owner.setAddress(testDataTokens[2]); owner.setCity(testDataTokens[3]); owner.setTelephone(testDataTokens[4]); return Optional.of(owner); }}
23
Copyright © 2016 Accenture All rights reserved.
Tip #2 – Test Data Creation• For each line in the file, a new owner will be created. To keep things
clean, let’s separate test data logic from the actual test stepsprivate void createNewUserBulk(final WebDriver driver, final String baseUrl) {
Optional<List<String>> testData = readTestData(); if (testData.isPresent()) { for (String testDataLine : testData.get()) { Optional<Owner> owner = parseOwner(testDataLine); if (owner.isPresent()) { createNewUser(driver, baseUrl, owner.get()); } } }}
24
Copyright © 2016 Accenture All rights reserved.
Tip #2 – Test Data Creation• The actual test steps are very, very simple – just access the form,
populate the data, and click the buttonprivate void createNewUser( final WebDriver driver, final String baseUrl, Owner owner) {
driver.get(baseUrl + "/owners/new");
driver.findElement(By.id("firstName")).sendKeys(owner.getFirstName()); driver.findElement(By.id("lastName")).sendKeys(owner.getLastName()); driver.findElement(By.id("address")).sendKeys(owner.getAddress()); driver.findElement(By.id("city")).sendKeys(owner.getCity()); driver.findElement(By.id("telephone")).sendKeys(owner.getTelephone());
driver.findElement(By.id("addowner")).click();}
25
Copyright © 2016 Accenture All rights reserved.
Tip #2 – Test Data Creation• Adding assertions is simple and will transform the useful test data
creation script into a functional test case (even more useful)private void createNewUser( final WebDriver driver, final String baseUrl, Owner owner) {
... driver.findElement(By.id("addowner")).click();
assertTrue(driver.getPageSource().contains(owner.getFirstName())); assertTrue(driver.getPageSource().contains(owner.getLastName())); assertTrue(driver.getPageSource().contains(owner.getAddress())); assertTrue(driver.getPageSource().contains(owner.getCity())); assertTrue(driver.getPageSource().contains(owner.getTelephone()));}
26
Copyright © 2016 Accenture All rights reserved.
Demo #2• How to create multiple owners for the Pet Clinic application
27
Copyright © 2016 Accenture All rights reserved.
Tip #3Decouple driver/browser logic from test logic
28
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• This will make tests reusable across browsers• The election of which browsers will be used can be done externally
and passed as parameter to the test• Using environment variables• Using Java VM system properties
• Will work in 99% of test cases• WebDriver API already decouples core from browser implementations• In some corner cases, driver specific API idiom will be needed
29
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• An exemplar implementation in Jorge’s post here:
• Four steps:• Step 1) Define some variables to hold desired setup• Step 2) Read values from environment variables and/or system properties• Step 3) Create test methods per browser and execute/skip test logic based on
the desired setup• Step 4) Create test logic methods that receive a Driver and URL as parameters
https://deors.wordpress.com/2013/02/18/selectable-selenium/
30
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• Step 1) Define some variables to hold desired setup
private static boolean RUN_HTMLUNIT;private static boolean RUN_FIREFOX;private static boolean RUN_CHROME;private static boolean RUN_OPERA;private static boolean RUN_IE;
private static String SELENIUM_HUB_URL;private static String TARGET_SERVER_URL;
31
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• Step 2) Read values from environment variables and/or system
properties (in a method annotated with @BeforeClass)RUN_HTMLUNIT = getConfigurationProperty( "RUN_HTMLUNIT", "test.run.htmlunit", true);
logger.info("running the tests in HtmlUnit: " + RUN_HTMLUNIT);
TARGET_SERVER_URL = getConfigurationProperty( "TARGET_SERVER_URL", "test.target.server.url", "http://localhost:58080/petclinic");
logger.info("using target server at: " + TARGET_SERVER_URL);
32
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• More on Step 2) All properties should have sensible defaults. For
example:• HtmlUnit browser, which is an in-memory browser, enabled by default• All other browsers, which may or may not be present, disabled by default• Selenium grid, running on localhost by default• Application to be tested, running on localhost by default
• The default settings proposed above allows for easy test execution, without further concern, in a developer workstation
33
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• Step 3) Create test methods (annotated with @Test) per browser and
execute/skip test logic based on the desired setup@Testpublic void testChrome() throws MalformedURLException, IOException {
Assume.assumeTrue(RUN_CHROME); try { Capabilities browser = DesiredCapabilities.chrome(); WebDriver driver = new RemoteWebDriver(new URL(SELENIUM_HUB_URL), browser); testNewPetFirstVisit(driver, TARGET_SERVER_URL); } finally { if (driver != null) { driver.quit(); } }}
34
Copyright © 2016 Accenture All rights reserved.
Tip #3 – Decouple Driver/Browser Logic• Step 4) Create test logic methods (not annotated) that receive a
Driver and the base URL where the app is deployed as parameterspublic void testNewPetFirstVisit(final WebDriver driver, final String baseUrl) {
driver.get(baseUrl);
WebElement findOwnerLink = driver.findElement(By.linkText("Find owner")); findOwnerLink.click();
driver.findElement(By.id("lastName")).clear(); driver.findElement(By.id("lastName")).sendKeys("Schroeder");
driver.findElement(By.id("findowners")).click();
...} 35
Copyright © 2016 Accenture All rights reserved.
Running Selenium (revisited)When the tests are executed, parameters will be read, if any. If not, sensible defaults will be applied, and tests executed.Some examples:• Run the tests in HtmlUnit, Chrome and Firefox, with Selenium grid and
application both deployed at localhost
• Run the tests in Chrome only, with Selenium grid and application deployed at some remote (shared) servers
-Dtest.run.chrome=true -Dtest.run.firefox=true
-Dtest.run.htmlunit=false -Dtest.run.chrome=true \ -Dtest.selenium.hub.url=http://selenium.test.acme.com:4444/wd/hub \ -Dtest.target.server.url=http://appserver.test.acme.com:58080/petclinic
36
Copyright © 2016 Accenture All rights reserved.
Running Selenium (revisited)If using Apache Maven, Failsafe plug-in should be used for integration tests instead of Surefire (which should be used only for unit tests)• Easy separation of unit test and integration test results• Great if used along a code coverage tool like JaCoCo and a quality dashboard
like SonarQube• Remember to pass test parameters embedded in argLine parameter so they
are passed appropriately to the JUnit test class:mvn failsafe-integration-test -DargLine=" \ -Dtest.run.htmlunit=false -Dtest.run.chrome=true \ -Dtest.selenium.hub.url=http://selenium.test.acme.com:4444/wd/hub \ -Dtest.target.server.url=http://appserver.test.acme.com:58080/petclinic"
37
Copyright © 2016 Accenture All rights reserved.
Running Selenium (revisited)This is an example of a continuous delivery pipeline in Jenkins which is using separate build jobs for unit and integration testsInternally it is using both Maven Surefire and Failsafe to differentiate both suite executions
Test results can be used as a quality gate along the pipeline
38
Copyright © 2016 Accenture All rights reserved. 39
YOU SHALL NOT PASS!
Image © New Line Cinema: “The Lord of the Rings – The Fellowship of the Ring”
Copyright © 2016 Accenture All rights reserved.
Running Selenium (revisited)This is an example of how using both Maven Surefire and Failsafe allows for better reportingTools, like SonarQube, which are aware of the difference between unit and integration tests, can show coverage metrics per kind of test, and combine all for overall value
40
Copyright © 2016 Accenture All rights reserved.
Demo #3• Working with multiple browsers from the IDE• Role of Selenium in continuous delivery pipelines
41
Copyright © 2016 Accenture All rights reserved.
Tip #4Use Wait conditions for quicker and more robust tests
42
Copyright © 2016 Accenture All rights reserved.
Tip #4 – Fluent Wait Conditions• When at some point of the test, it is needed to wait for something to
happen• Navigating to a new page• Waiting for the server-side transaction to finish• Waiting for an AJAX call
• Don’t be tempted by the dark side – never use Thread.sleep() or similar idiom• If child process finishes sooner, time is wasted – scripts run slower
• WebDriver API provides with a more elegant approach – WebDriverWait
43
Copyright © 2016 Accenture All rights reserved. 44Image credit: http://imgur.com/gallery/x9IkCWC
Copyright © 2016 Accenture All rights reserved.
Tip #4 – Fluent Wait Conditions• WebDriverWait instances are created for a specific WebDriver
instance, good if multiple tests are executed in parallel• The condition is passed as a Function or Predicate from Google Guava
(pre-Java 8 functional interfaces) so they are lambda-ready!• The Function will evaluate some condition, for example find some
element and return it, or check that page title is as expected• If the element is not found, the WebDriver API usual
NotFoundException is ignored until the timeout is over
45
Copyright © 2016 Accenture All rights reserved.
Tip #4 – Fluent Wait Conditions• This is an example using pre-lambda syntax
driver.get(baseUrl);
// wait for the application to get fully loadedWebElement findOwnerLink = (new WebDriverWait(driver, 5)).until( new ExpectedCondition<WebElement>() {
public WebElement apply(WebDriver d) { return d.findElement(By.linkText("Find owner")); }});
findOwnerLink.click();
...rest of script
46
Copyright © 2016 Accenture All rights reserved.
Tip #4 – Fluent Wait Conditions• This is the very same example but using a lambda expression
• The cast is needed so the compiler knows which functional interface is being used (Function or Predicate) as until() method is overloaded
driver.get(baseUrl);
// wait for the application to get fully loadedWebElement findOwnerLink = (new WebDriverWait(driver, 5)).until( (Function<WebDriver, WebElement>) d -> d.findElement(By.linkText("Find owner")));findOwnerLink.click();
...rest of script
47
Copyright © 2016 Accenture All rights reserved.
Tip #4 – Fluent Wait Conditions• This is another example using a Predicate as a lambda expression
• The cast is, once again, needed
...rest of script
findOwnerLink.click();
(new WebDriverWait(driver, 5)).until((Predicate<WebDriver>) d -> d.getCurrentUrl().startsWith(baseUrl + "/owners/search"));
...rest of script
48
Copyright © 2016 Accenture All rights reserved.
Demo #4• Focus on WebDriver fluent wait conditions
49
Copyright © 2016 Accenture All rights reserved.
Tip #5Page Object idiom for highly reusable test scripts
50
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• Testing is about sending data, and verifying responses• A test script should only contain details about data and operations,
and code to check the responses• Implement an unique way for accessing each element in your page• Decouple test code from the way you access elements from the UI• Avoid reworking effort when your application changes• Reduce duplicated code
51
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object IdiomPage Object Pattern
• An abstraction of the web pages under test• Encapsulates the code to access the web elements of the UI• Provides an API that facilitates the creation of test cases even for
people who may not understand about the implementation• Single point for rework• Will make your test code more readable
52
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• Implement a constructor with the driver and the baseURL as the
parameter
public OwnerPage(WebDriver driver, String baseUrl) {
this.driver = driver; this.baseUrl = baseUrl; }
53
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• Define the elements in the page
private By titleElement = By.xpath("//h2");
private By mainContent = By.id("main");
private By editOwnerCommand = By.linkText("Edit Owner");
private By addNewPetCommand = By.linkText("Add New Pet");
private By editPetCommand = By.linkText("Edit Pet");
private By addVisitCommand = By.linkText("Add Visit");
private By linkToHome = By.linkText("Home");
private By nameTag = By.xpath("//table[1]/tbody/tr[1]/td"); //sorry about using the DOM this time...
private By addressTag = By.xpath("//table[1]/tbody/tr[2]/td"); //please don’t do this...
54
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• Provide the public API for accessing the elements in your page
public String getName() {
WebElement element = driver.findElement(nameTag); String name = element.getText(); return name; }
public String getAddress() {
WebElement element = driver.findElement(addressTag); String address = element.getText(); return address; }
55
56Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• …and for actions the user will do in the page under test
public PetPage navigateToEditPet(String petId) {
driver.findElement(editPetCommand).click();
(new WebDriverWait(driver, 5)).until( (Predicate<WebDriver>) d -> getUrl().startsWith(baseUrl + "/owners/"+petId+"/pets") && getUrl().contains("edit"));
return new PetPage(driver, baseUrl); }
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• The test code should be something like
driver.get(baseUrl);
HomePage homePage = new HomePage(driver, baseUrl);
FindOwnersPage findOwnersPage = homePage.navigateToFindOwners();
OwnerPage ownerPage = findOwnersPage.findOwner("Schroeder"); assertTrue(ownerPage.getName().contains("David Schroeder"));
PetPage petPage = ownerPage.navigateToAddNewPet();
petPage.createPet("Mimi", "2011-10-02"); assertTrue(ownerPage.getName().contains("David Schroeder")); assertTrue(ownerPage.getMainText().contains("Mimi")); assertTrue(ownerPage.getMainText().contains("2011-10-02"));
57
Copyright © 2016 Accenture All rights reserved.
Tip #5 – Page Object Idiom• Before and after
driver.get(baseUrl);
HomePage homePage = new HomePage(driver, baseUrl);
FindOwnersPage findOwnersPage = homePage.navigateToFindOwners();
OwnerPage ownerPage = findOwnersPage.findOwner("Schroeder"); assertTrue(ownerPage.getName().contains("David Schroeder"));
driver.get(baseUrl);
WebElement findOwnerLink = (new WebDriverWait(driver, 5)) .until((Function<WebDriver, WebElement>) d -> d.findElement(By.linkText("Find owner")));
findOwnerLink.click();
(new WebDriverWait(driver, 5)).until((Predicate<WebDriver>) d -> d.getCurrentUrl().startsWith(baseUrl + "/owners/search"));
driver.findElement(By.id("lastName")).clear();driver.findElement(By.id("lastName")).sendKeys("Schroeder");driver.findElement(By.id("findowners")).click();
(new WebDriverWait(driver, 5)).until((Predicate<WebDriver>) d -> d.getCurrentUrl().equals(baseUrl + "/owners/9"));
assertTrue(driver.findElement(By.id("main")).getText().contains("David Schroeder"));
58
Copyright © 2016 Accenture All rights reserved.
Demo #5• Writing tests using Page Objects
59
Copyright © 2016 Accenture All rights reserved.
Tip #5+Using the PageFactory with Page Object Design Pattern
60
Copyright © 2016 Accenture All rights reserved.
Tip #5+ – The PageFactory• Use the PageFactory to initialize the elements of the page under test
import org.openqa.selenium.support.FindBy;import org.openqa.selenium.support.PageFactory;import org.openqa.selenium.support.ui.WebDriverWait;
...
public OwnerPageFactory(WebDriver driver, String baseUrl) {
this.driver = driver; this.baseUrl = baseUrl; PageFactory.initElements(driver, this); }
61
Copyright © 2016 Accenture All rights reserved.
Tip #5+ – The PageFactory• Use the FindBy annotation while defining the WebElements
@FindBy(id="main")private WebElement mainContent;
@FindBy(linkText="Edit Owner")private By editOwnerCommand;
@FindBy(linkText="Add New Pet")private WebElement addNewPetCommand;
@FindBy(linkText="Edit Pet")private WebElement editPetCommand;
@FindBy(linkText="Add Visit")private WebElement addVisitCommand;
62
Copyright © 2016 Accenture All rights reserved.
Tip #5+ – The PageFactory• Your test could be something like…
public PetPage navigateToEditPet(String petId) {
editPetCommand.click();
(new WebDriverWait(driver, 5)).until((Predicate<WebDriver>) d -> getUrl().startsWith(baseUrl + "/owners/"+petId+"/pets") && getUrl().contains("edit"));
return new PetPage(driver, baseUrl); }
63
Copyright © 2016 Accenture All rights reserved.
Demo #5+• The Page Object design pattern and the PageFactory
64
Copyright © 2016 Accenture All rights reserved.
Tip #6Working with Absolute Coordinates
65
Copyright © 2016 Accenture All rights reserved.
Tip #6 – Working with Absolute Coordinates• As with Tip #1, this is a technique we do not recommend• It makes tests brittle• Element locations are likely to change in different browsers• Even more difficult (and less useful) with responsive pages• Only as a last resort mechanism
• In Selenium it is not directly possible to locate an element at some point in page• However, it is possible to traverse a list of elements, ask for each
element location and size, and identify whether an element is under one specific point in page
66
Copyright © 2016 Accenture All rights reserved.
Tip #6 – Working with Absolute Coordinates• To ask for location and size, use this WebDriver methods:
• For example:
WebElement.getLocation();WebElement.getSize();
List<WebElement> elements = driver.findElements(By....);for (WebElement e: elements) { Point loc = e.getLocation(); Dimension size = e.getSize(); if (x >= loc.getX() && x <= loc.getX() + size.getWidth() && y >= loc.getY() && y <= loc.getY() + size.getHeight()) {
return e; }}
67
Copyright © 2016 Accenture All rights reserved.
Tip #6 – Working with Absolute Coordinates• Having that multiple elements can be at the same physical location• Exercise caution• Do not traverse all page elements during a search• Select carefully which elements to use: by class, tag name or XPath (DOM)
• For example, in Pet Clinic home, H2 and IMG elements overlap
68
Copyright © 2016 Accenture All rights reserved.
Demo #6• Working with absolute coordinates
69
Copyright © 2016 Accenture All rights reserved.
Q & AWe wish you enjoyed the talk
Visit https://github.com/deors/deors.demos.petclinic to get the code
70
Jorge Hidalgo @_deors
Vicente González @viarellano