Showing posts with label learn Selenium WebDriver. Show all posts
Showing posts with label learn Selenium WebDriver. Show all posts

How to Learn Selenium WebDriver in 3 Days

or How to Learn Selenium WebDriver like a JEDI


I like Star Wars movies.

I still remember seeing the first episode, A New Hope, at the cinema, back in the 1980s.

Since then, I watched it and the following 5 episodes, produced by LucasFilm, many, many times.

The new ones, produced by Disney, are still good (much simpler though) especially Rogue One and The Last Jedi.





There is an interesting difference between the old Star Wars movies and the recent ones.

In The Last Jedi, Luke Skywalker agrees to teach Rey just 3 lessons about the force. She needs to learn how to be a JEDI to be able to fight the imperial army and yes, Kylo Ren.

The lessons happen fast and only bits of knowledge and wisdom go from the teacher to the student. Not much skills as Luke is now a wise old man instead of a JEDI knight as before.

Just 3 lessons are enough for Rey to discover her potential and the mighty force. Having the force, she lifts a huge pile of rocks that stops her friends from running from the imperial army.

Unfortunately, as the episode title explains, the last JEDI disappears so in the future, there are no more JEDIs to teach about the force.

No more lessons about the force but probably the lessons are not needed any longer.

If Rey can learn the force in 3 lessons, maybe she can explain it to others in 1 lesson or maybe in just a few sentences.

The force is no longer accessible to a few but to all.
You just need to put your mind to it and there it is.


Now, why shouldnt we be able to do the same with Selenium WebDriver?

3 lessons.

It is not that hard.

The secret of the force is in how the study material is broken down per day.

Day 1
learn about html, xpath, css, browser dom, eclipse, java basics, unit testing.

Day 2
learn about object oriented notions like classes, objects, inheritance, composition, interfaces, polymorphism, generics, predicates, reflection, streams.

Day 3
learn the page object model, page factory, loadable component, slow loadable component and a few other design patterns.


And that's it, we are there, we can lift our own automation rocks.

This is totally doable if you are a Star Wars believer.

Everyone can do whatever they put their mind to.
Everything is easy to do and learn.
Everything is accessible to all of us, even the force.
The JEDI knights go away and who need them anyways?


Now, the old Star Wars episodes are a little different from the new ones.

In The Phantom Menace and Attack Of The Clones, the process of becoming a JEDI knight is explained in detail. You had to start your training at the JEDI Academy at a young age and complete it.



Then, a mentor would guide you through real life experiences until you are ready to pass the JEDI trials for becoming a knight.

No one became a JEDI in 3 lessons or 6 or 15.
No matter how skilled, talented or gifted.

Instead, the trainee would go through a long, difficult and intensive education with both teachers, peers and mentors.

This would totally work for Selenium WebDriver as well.

Selenium WebDriver learning is basically learning how to code which is best done with teachers, peers and mentors.

So Star Wars is a viable source of inspiration.

You just have to watch the proper episodes.

Watch The Last Jedi and believe that 3 lessons are sufficient.

Watch The Phantom Menace and understand that 6 months is a better time range.



SeleniumJava.com post - How to deal with windows authentication popups

If you dont like Bugs Bunny, maybe you should not continue reading.

What is the first thing that I do every morning at work?
Check the results of the automation scripts executed in Jenkins over night.
I want to know how many scripts passed or failed because of the work done in the previous day.
Most days, some scripts pass and some fail.
But recently, most scripts failed.
This was very unusual so I looked into it right away.
All failures followed the same pattern: the scripts failed on opening the site in the browser.



Where can I find a full test automation project with Selenium WebDriver?

You have been learning test automation with Selenium.

You went through lots of concepts and would like to see how they all work together.

Where can you find a project that uses everything you learned and more?

Here :)

What follows is a small project that I built a while ago for a job interview.

It uses many test automation concepts such as:
  • page factory
  • base classes
  • html classes
  • test listeners
  • test ng assertions and fixtures
  • annotations
  • custom locators (javascript and jquery)
  • screenshots
  • saving errors in text files


The exercise consisted in automating the following test case with Java and Selenium WebDriver:

  • Launch bestbuy url (www.bestbuy.ca)
  • Search a product and add it to cart
  • Go all the way through checkout process and place the order with invalid credit card
  • Capture the error message due to invalid credit card


Before downloading the project and checking the source code, a few details about the project.

Project details

Maven project
- all dependencies are managed through the pom.xml file

Test NG
- unit testing library

Java JDK 8
- used for lambda expressions and streams

Page Factory
- pattern for creating page object and page fragment classes
- the elements of page object/fragment classes have names and locators
- names and locators are implemented using annotations
- available locator types are id, xpath, css, name and javascript
  
   @Name("SEARCH_HEADER")
   @FindBy(className = "main-navigation-container") 
   public class SearchHeader extends HtmlElement{ 
 
   @Name("SEARCH_FIELD")
   @FindBy(id = "ctl00_MasterHeader_ctl00_uchead_GlobalSearchUC_TxtSearchKeyword") 
   private TextInput searchKeywordTxt;
 
   @Name("SEARCH_BUTTON")
   @FindBy(id = "ctl00_MasterHeader_ctl00_uchead_GlobalSearchUC_BtnSubmitSearch")
   private Button searchBtn; 
     
   public void search(String keyword) {
     searchKeywordTxt.click();  
     searchKeywordTxt.clear();
     searchKeywordTxt.sendKeys(keyword);
     searchBtn.click();  
   }
}


The project has the automation framework classes in the main folder and all test items in the test folder.

Main folder (framework classes)

annotations classes
- FindBy
- FindByJQUERY
- FindByJS
- Name
- Timeout
 
 package com.bestbuy.demo.annotations;

 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;

 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE, ElementType.FIELD})
 public @interface Name {
   String value();
 }

html element classes
 
    package com.bestbuy.demo.element;

    import org.openqa.selenium.By;
    import org.openqa.selenium.NoSuchElementException;
    import org.openqa.selenium.WebElement;

    public class CheckBox extends TypifiedElement {
   
      public CheckBox(WebElement wrappedElement) {
        super(wrappedElement);
      }

      public WebElement getLabel() {
        try {
            return getWrappedElement().findElement(By.xpath("following-sibling::label"));
        } catch (NoSuchElementException e) {
            return null;
        }
      }

      public String getLabelText() {
        WebElement label = getLabel();
        return label == null ? null : label.getText();
      }

      public String getText() {
        return getLabelText();
      }

      public void select() {
        if (!isSelected()) 
            getWrappedElement().click();        
      }

      public void deselect() {
        if (isSelected()) 
            getWrappedElement().click();
      }

      public void set(boolean value) {
        if (value) 
           select();
        else 
           deselect();
      }
   }

  
exceptions classes

decorator and proxy classes used by the page factory

page class
- used as base class for the page object classes

page factory classes

miscellaneous classes such as
- custom driver class, screenshot class
- enumerations (used to avoid hardcoding data in the page objects)
- simple logger class
- property class
- TextFile class

Test folder (test items)

  • base test class (used as base by the test classes)

  • page objects classes (all page object and page fragment classes)

  • test listeners (class for taking a screenshot and logging exceptions in case of failures)
  •  
     package com.bestbuy.demotests.testlisteners;
    
     import java.lang.reflect.Field;
     import org.testng.ITestContext;
     import org.testng.ITestListener;
     import org.testng.ITestResult;
     import com.bestbuy.demo.exceptions.HtmlElementsException;
     import com.bestbuy.demo.utils.Driver.BrowserDriver;
     import com.bestbuy.demo.utils.Driver.Screenshot;
     import org.openqa.selenium.WebDriver;
     import static com.bestbuy.demotests.BaseTest.BaseTestClass.*;
    
     public class TestListener implements ITestListener {   
       
        @Override
        public void onTestFailure(ITestResult result) {       
           try {
             Screenshot screenshot = 
             new Screenshot(getDriverFromBaseTest(result));
       
             screenshot.capture(result.getName());
           } 
           catch (Exception ex) {
             throw new HtmlElementsException(ex.getMessage());
           }                 
       }  
    
       @SuppressWarnings("unchecked")
       private WebDriver getDriverFromBaseTest(ITestResult result) 
           throws IllegalAccessException {
       
          WebDriver driver = null;
       
          try { 
             Class< ? extends ITestResult> testClass = 
             (Class< ? extends ITestResult>) result.getInstance().getClass();
      
             Class< ?extends ITestResult> baseTestClass = 
             (Class< ? extends ITestResult>) testClass.getSuperclass();
      
             Field driverField = baseTestClass.getDeclaredField("driver");
        
             driver = (BrowserDriver)driverField.get(result.getInstance()); 
        
             return driver;
         } 
         catch (SecurityException | NoSuchFieldException | IllegalArgumentException ex) {     
             throw new HtmlElementsException("error getting the driver from base test");    
         }         
       
       }
        
       @Override
       public void onTestSuccess(ITestResult result) 
       {}  
      
       @Override
       public void onTestSkipped(ITestResult result) 
       {} 
      
       @Override
       public void onTestFailedButWithinSuccessPercentage(ITestResult result) 
       {} 
      
       @Override
       public void onStart(ITestContext context)     
       {} 
      
       @Override 
       public void onFinish(ITestContext context)    
       {}
      
       @Override
       public void onTestStart(ITestResult arg0)
       {}
        
     }
    
  • test class


Most interactions with web elements are done through the page factory classes.

Occasionally, Javascript code is used when buttons cannot be clicked through the page factory.

Some of the pages have random popups that are closed if displayed.

The test script does the following:
  1. Open the home page
  2. Execute a keyword search
  3. Select the Online Only filter
  4. Select a product with online availability
  5. Add the product to the cart
  6. Continue checkout on the basket page
  7. Select New Member
  8. Fill in the Ship To info
  9. Select credit card as payment method
  10. Fill in the payment info
  11. Submits transaction
  12. Verify that there is at least an error message
  13. Saves the error messages and the search keyword to a text file


The test script uses a keyword parameter which can take multiple values.


Download source code

Interested in learning how to build a project like this one?

Get started here


Best Resource for Improving Selenium Skills? Selenium code

You learned Selenium and Java test automation basics from a book, online course or youtube. 

Page object model is understood, locators are clear, explicit waits and expected conditions are straightforward.

Java concepts such as classes, objects, inheritance, looping, arrays, lists are all covered.

With the new skills, you can write test automation scripts for a variety of websites.

You are wondering now about the next level of test automation.

There is always a next level so what is it for you?

How do you go to it?

What resource should you use for guidance?





Most available books and courses target the beginner and intermediate test automation levels.

There are not many resources available on advanced test automation skills.

There is one place however, often forgotten, that has everything you need in your quest to more knowledge.

The Selenium source code.

It is extremely helpful not only to learn how to use Selenium but also to read the source code.

Doing this provides many benefits such as:

1. it shows how Selenium classes and methods work

For example, how does Selenium find a web element using the findElement() method?
How does Selenium locate an element by css locator?

2. you can learn how a framework is built

Selenium is a framework.

You can learn many things about building an automation framework from studying how the Selenium framework is built.

3. you can learn what good programming is

Selenium was created by professional developers.

What better source of learning than the code that they wrote?

4. you find out what you dont know yet but should know

For example, by reading the Page Factory code, you will learn about annotations.

And reflection.

And design patterns.

And proxy classes.

And generics.

And interfaces.

You will need all these Java concepts (and more) to make the transition to the next test automation level.

So attach the Selenium source code to the project in Eclipse and start reading.




When do you need these new skills?

Lets say that you want to have in your automation project the ability of taking screenshots automatically when an exception happens in a test script.

You use TestNG as the unit testing framework.

TestNG listeners can help but there is a problem.

You need in the test listener the driver object created in the test class or the base test class.

To get this driver object, you will use reflection and generics. 

Another example of using the new skills is creating new locator types.

Selenium provides by default a variety of locator types such as id, class, name, xpath, css.

How about using also Jquery and Javascript locators?

You will need annotations for this.


How to interact with sliders in Selenium WebDriver



Sliders allow users to select a value by dragging and dropping a handle. 

This value can be a price, a quantity, a year. 

The web page could use a textbox for getting the same information from users. 

But with sliders, the page becomes much more interesting.

Read the full article on seleniumjava.com.

Back To Basics - How does WebDriver driver = new FirefoxDriver(); work?




Have you ever thought how this line works?


WebDriver driver = new FirefoxDriver();


More specifically, why are 2 types needed for creating the driver object?

And why is it possible to create a WebDriver variable but instantiate it with a FirefoxDriver object?

You can find the answer here.

Shortcuts and Tips for Test Automation Beginners




Euclid, the inventor of geometry, was asked once by the ruler of Greece if there is a shorter road for learning geometry. 

There is no royal road to geometry, Euclid replied. 

There are no shortcuts to test automation with 
Selenium. 

Tips are easier to give. 

So, before starting test automation, 
  • learn the programming language well
  • learn how to write clean code with your programming language






Beginner's Guide To Explicit Waits




You may have noticed already that some of the Selenium articles are moved from this blog to another: seleniumjava.com.

Going forward, Selenium articles will be published on seleniumjava.com and all other articles on this blog.

The newest Selenium article is a Beginner's Guide To Explicit Waits.

Explicit waits should be used in test automation scripts when the script interacts with web elements as they provide a way of synchronizing the script with the web site.

The article goes over

  • why explicit waits are needed
  • how explicit waits work
  • when explicit waits can be used
  • how explicit waits can be used for 
    • finding single web elements
    • finding multiple web elements
    • checking page title and url
    • checking element's status


Hope you like it!




Do Cross Browser Test Automation Like A PRO - Part 2


Part 1 showed how to run a test script in a local browser.

In Part 2, we  change the code so that the test script runs in multiple local browsers.

See below how each class changes.





TEST CLASS


The test class needs the following changes:
  • @RunWith(value = Parameterized.class) annotation is added before the class so that the class can have test scripts with parameters
  • browserName member is added as parameter of the test script; it uses the @Parameter annotation
  • the public static Collection object data () specifies the values that the browserName parameter can take
  • the Driver.initialize(browserName) method uses the browserName parameter 


package TESTS;
import static org.junit.Assert.*;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Collection;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import FRAMEWORK.Driver;
import FRAMEWORK.HomePage;
import FRAMEWORK.ResultsPage;

@RunWith(value = Parameterized.class)
public class TestScripts_LocalBrowsers {                  

@Parameter
public String browserName;   

@Parameters
public static Collection object data () {
     
Object[][] data = {{"Firefox"}, {"Chrome"}, {"InternetExplorer"}};

return Arrays.asList(data);   

} 

@Before
public void setUp() throws MalformedURLException 
{   

Driver.initialize(browserName); 

}

@After
public void tearDown()
{   

Driver.getDriver().quit();  

}

@Test
public void testResultsInfo() throws InterruptedException
{        

ResultsPage results = (new HomePage()).search();    
     
assertTrue(results.isKeywordDisplayed() == true);                     

assertTrue(results.resultsCount() > 0);             

}   

}



DRIVER CLASS


The driver class needs the following changes:
  • a new initialize(browser) method is added; the new method uses a parameter for the browserName
  • the initialize(browser) method creates the browser driver that corresponds to the browser name



package FRAMEWORK;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.remote.DesiredCapabilities;

public class Driver { 

private static WebDriver driver;  

public static WebDriver getDriver()
{

return driver;

}

public static void initialize() 
{      

driver = getFirefoxDriver();

}

public static void quit() 
{      

getDriver().quit();

}

public static void initialize(String browser) throws MalformedURLException
{      

if (browser.equalsIgnoreCase("Chrome")) 
{

driver = getChromeDriver();
return;

}

if (browser.equalsIgnoreCase("InternetExplorer"))
{

driver = getInternetExplorerDriver();
return;

}

if (browser.equalsIgnoreCase("Firefox"))
{

driver = getFirefoxDriver();
return;

}

throw new NoSuchElementException("Invalid browser name");

}   

private static WebDriver getChromeDriver()
{

System.setProperty("webdriver.chrome.driver", 
"C:\\Selenium\\BrowserDrivers\\chromedriver.exe");

return  new ChromeDriver(); 

}

private static WebDriver getFirefoxDriver()
{    

return new FirefoxDriver();

}

private static WebDriver getInternetExplorerDriver()
{  

System.setProperty("webdriver.ie.driver", 
"C:\\Selenium\\BrowserDrivers\\IEDriverServer.exe");

return  new InternetExplorerDriver();     

}      

}



HOME PAGE CLASS


The home page class has only one change.

It uses By locators instead of String locators.

It uses By.id and By.name when possible.


package FRAMEWORK;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class HomePage {

By searchMenuItemLocator = By.id("search-cta");
By searchTextBoxLocator  = By.id("searchinput");
By searchButtonLocator   = By.name("simplesearch");  

WebDriver driver; 
WebDriverWait wait;               

public HomePage()
{  

this.driver = Driver.getDriver();
wait = new WebDriverWait(driver, 10);

driver.get(Settings.siteUrl);

wait.until(ExpectedConditions.titleIs(Settings.homePageTitle));

}   

public ResultsPage search()
{

wait.until(ExpectedConditions.
elementToBeClickable(searchMenuItemLocator)).
click();

wait.until(ExpectedConditions.
elementToBeClickable(searchTextBoxLocator)).
sendKeys(Settings.keyword);

wait.until(ExpectedConditions.
elementToBeClickable(searchButtonLocator)).
click();    

return new ResultsPage();  

}     

}



RESULTS PAGE CLASS


The results page class has only one change.

It uses By locators instead of String locators.

It uses By.id and By.name when possible.


package FRAMEWORK;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class ResultsPage { 

By searchKeywordLocator = By.xpath("//h2[@class='product-search']/a"); 
By resultsCountLocator  = By.xpath("(//div[@class='resultshits'])[1]");

WebDriver driver; 
WebDriverWait wait; 

public ResultsPage()
{ 

this.driver = Driver.getDriver();
wait = new WebDriverWait(driver, 10);

wait.until(ExpectedConditions.titleIs(Settings.resultsPageTitle));

}    

public Boolean isKeywordDisplayed()
{ 

return wait.until(ExpectedConditions.
visibilityOfElementLocated(searchKeywordLocator)).
isDisplayed(); 

}    

public int resultsCount()
{     

WebElement resultCountElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(resultsCountLocator));

return Integer.parseInt(extractValue(resultCountElement.getText(), Settings.resultsCountRegEx, 3)); 

}

public String extractValue(String textValue, String regEx, int position)
{ 

String result = "";

Pattern pattern = Pattern.compile(regEx);

Matcher matcher = pattern.matcher(textValue);

if (matcher.find())

result = matcher.group(position); 

return result;

} 

}


Part 3 will change the code more so that the test script runs in a cloud Selenium Grid.


If you have any questions about how the code works or any difficulties running it, please post them in the Comments section.



Do Cross Browser Test Automation Like A PRO



This article is the first from a series about how to do cross browser test automation with Selenium WebDriver.

The series has 3 parts.

Part 1 explains how to run test scripts for one browser.

Part 2 goes into running test scripts on multiple local browsers.

Part 3 concludes with running test scripts in a cloud Selenium grid.


TEST CASE TO BE AUTOMATED

I will use the www.londondrugs.com site.

The test case to be automated consists in

1. opening the home page of the site




2. clicking on the SEARCH option of the top menu


3. typing a keyword in the SEARCH textbox


4. clicking the submit button





5. checking that the keyword is included in the results page


6. checking that the results count on the results page is greater than 0










The project uses the following architecture:

The test scripts are created in the test class that belongs to the TESTS layer (of the automation architecture).

The test scripts do not use directly any Selenium WebDriver classes and do not declare a WebDriver object.

They use the page object classes instead.

There are 2 page object classes: Home Page class, Results Page class


They belong to the FRAMEWORK layer of the automation architecture.

Both page object classes implement user actions for each site page by using Selenium WebDriver classes (which correspond to the SELENIUM layer of the automation architecture).

The WebDriver object is being created by a Driver class that belongs to the FRAMEWORK layer as well.

The project uses 2 folders:

TESTS folder: TestScripts_OneBrowser class

FRAMEWORK folder: Settings class,  HomePage class, ResultsPage class,  Driver class



SETTINGS.JAVA



The purpose of this class is to store all settings needed in the project such as

  • site url
  • keyword search
  • page title
  • regular expression used for extracting the results number

public class Settings {

public final static String siteUrl = "http://www.londondrugs.com";    

public final static String keyword = "iphone";

public final static String homePageTitle = "London Drugs";

public final static String resultsPageTitle = "Sites-LondonDrugs-Site";

public final static String resultsCountRegEx = "(.*)(of )(\\d+)( matches)";

}


DRIVER.JAVA


This class creates the browser driver and stores it into a static class member.

By default, the browser driver is for Firefox.

The driver object is created in a static initialize() method.

The driver object is closed in a static quit() method.

The initialize() and quit() methods are created as static so that
  • there is no need of declaring a WebDriver object in the test class
  • the page object classes do not need to pass the driver object from one to another


import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.Platform;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
package FRAMEWORK;

public class Driver { 

private static WebDriver driver;

public static WebDriver getDriver()
{

return driver;

}

public static void initialize() 
{      

driver = getFirefoxDriver();

} 

public static void quit()
{

getDriver().quit();

}

}


HOME PAGE OBJECT CLASS


The home page class declares a few locator members for the 
  • search menu item element
  • search text box element
  • submit button

It declares members for the driver and explicit wait objects that are initialized in the class constructor.

The class constructor opens the site's home page in the url and checks if the home page title is correct.

The home page class has also a search method that
  • clicks the search menu item
  • types the keyword in the search text box
  • clicks the submit button
The result of the search method is a ResultsPage object.


import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
package FRAMEWORK;

public class HomePage {

String searchMenuItemLocator = "//div[@id='search-cta']";
String searchTextBoxLocator  = "//input[@id='searchinput']";
String searchButtonLocator   = "//button[@name='simplesearch']";  

WebDriver driver; 
WebDriverWait wait;               

public HomePage()
{  

this.driver = Driver.getDriver();
wait = new WebDriverWait(driver, 10);

driver.get(Settings.siteUrl);

wait.until(ExpectedConditions.titleIs(Settings.homePageTitle));

}   

public ResultsPage search()
{

wait.until(ExpectedConditions.elementToBeClickable(By.xpath(searchMenuItemLocator))).
click();

wait.until(ExpectedConditions.elementToBeClickable(By.xpath(searchTextBoxLocator))).
sendKeys(Settings.keyword);

wait.until(ExpectedConditions.elementToBeClickable(By.xpath(searchButtonLocator))).
click();    

return new ResultsPage();  

}     

}


RESULTS PAGE OBJECT CLASS


The results page class declares a few locator members for the
  • search keyword element
  • results count element

It declares members for the driver and explicit wait objects that are initialized in the class constructor.

The class constructor checks if the results page title is correct.

The results page class has the following methods

- isKeywordDisplayed()

checks if the keyword is displayed in the results page

- resultsCount()

returns the number of results;
uses the extractValue method which uses a regular expression for extracting the results count

The results of the search method is a ResultsPage object.

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
package FRAMEWORK;

public class ResultsPage { 

String searchKeywordLocator = "//h2[@class='product-search']/a"; 
String resultsCountLocator  = "(//div[@class='resultshits'])[1]";

WebDriver driver; 
WebDriverWait wait; 

public ResultsPage()
{ 

this.driver = Driver.getDriver();
wait = new WebDriverWait(driver, 10);

wait.until(ExpectedConditions.titleIs(Settings.resultsPageTitle));

}    

public Boolean isKeywordDisplayed()
{ 

return wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath(searchKeywordLocator))).isDisplayed(); 

}    

public int resultsCount()
{     

WebElement resultCountElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath(resultsCountLocator)));

return Integer.parseInt(extractValue(resultCountElement.getText(),
Settings.resultsCountRegEx, 3));  

}

public String extractValue(String textValue, String regEx, int position)
{          

String result = "";

Pattern pattern = Pattern.compile(regEx);

Matcher matcher = pattern.matcher(textValue);

if (matcher.find())
result = matcher.group(position); 

return result; 

} 

}


TEST CLASS

It includes the following methods:

- setUp()

It uses the @Before annotation so it is executed automatically before the test script.
It initializes the browser driver.

- tearDown()

It uses the @After annotation so it is executed automatically after the test script.
It closes the browser driver.

- testResultsInfo()

It uses the HomePage and ResultsPage classes for implementing the test script.

import static org.junit.Assert.*;
import java.net.MalformedURLException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import FRAMEWORK.Driver;
import FRAMEWORK.HomePage;
import FRAMEWORK.ResultsPage;

public class TestScripts_OneBrowser {                  

@Before
public void setUp() throws MalformedURLException 
{      

Driver.initialize();   

}

@After
public void tearDown()
{    

Driver.getDriver().quit();   

}

@Test
public void testResultsInfo() throws InterruptedException
{        

ResultsPage results = (new HomePage()).search();    

assertTrue(results.isKeywordDisplayed() == true);            

assertTrue(results.resultsCount() > 0);            

}  

}

The code included in the article works.

If you have any difficulty using it or have any questions, please leave them in the Comments section.

Part 2 will show how to extend this code so that the test script runs in multiple local browsers.

Make Test Automation Scripts Fast Using HTML Parsing Frameworks



If test execution speed is most important, HTML parser libraries like JSOUP should be used when Selenium WebDriver scripts are too slow.

JSOUP is a Java library for working with real-world HTML.

It provides a very convenient API for extracting and manipulating data, using the best of DOM, CSS, and jquery-like methods.


Selenium WebDriver scripts are very slow

The speed of Selenium WebDriver scripts is very slow as it depends on:
  • browser load time; how fast the browser loads depends on how good the computer is
  • site's performance; the performance of the site depends on server hardware, number of concurrent users, site architecture
  • internet connection's performance

Let's take for example a simple script that implements the following test case:
  • open the home page of the http://www.vpl.ca site
  • do a keyword search
  • on the results page, click on the title link for the first result
  • on the details page, check that the book title and book author values exist

All code samples are just that, samples.

No page object classes are being used.

@Test
public void test2() throws InterruptedException {

driver.get("http://www.vpl.ca");


WebElement searchField = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//input[@id='globalQuery']")));


searchField.click();
searchField.sendKeys("java");

WebElement searchButton = wait.until(ExpectedConditions.
elementToBeClickable(By.xpath("//input[@class='search_button']")));


searchButton.click();

WebElement resultTitle = wait.until(ExpectedConditions.
elementToBeClickable(By.xpath("(//a[@testid='bib_link'])[1]")));


resultTitle.click();

String bookTitleValue = "", bookAuthorValue = "";;

WebElement bookTitleElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//h1[@id='item_bib_title']")));

bookTitleValue = bookTitleElement.getText();
assertTrue(bookTitleValue.length() > 0);

//some books do not have author so I need to use a try/catch
try {


WebElement bookAuthorElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//a[@testid='author_search']")));

bookAuthorValue = bookAuthorElement.getText();
assertTrue(bookAuthorValue.length() > 0);


}
catch(Exception e) { }

}


The test script runs correctly in about 15 seconds.

Let's assume that we want to create a script for another test case that does the same things as the previous one but for all book title links from the results page (10 links).

The script is a bit more complicated as it needs to iterate through all book title links:
  • open the home page of the http://www.vpl.ca site
  • do a keyword search
  • on the results page, do the following for each book title link
    • click on the title link
    • on the details page, check that the book title and book author values exist
    • go back
    • continue with the next link

@Test
public void test1() throws InterruptedException {

driver.get("http://www.vpl.ca");

WebElement searchField = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//input[@id='globalQuery']")));


searchField.click();
searchField.sendKeys("java");

WebElement searchButton = wait.until(ExpectedConditions.
elementToBeClickable(By.xpath("//input[@class='search_button']")));


searchButton.click();

for (int i = 1; i <= 10; i++) {

WebElement resultTitle = wait.until(ExpectedConditions.
elementToBeClickable(By.xpath("(//a[@testid='bib_link'])[" + i + "]")));


resultTitle.click();

String bookTitleValue = "", bookAuthorValue = "";

WebElement bookTitleElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//h1[@id='item_bib_title']")));


bookTitleValue = bookTitleElement.getText();

assertTrue(bookTitleValue.length() > 0);

try {

WebElement bookAuthorElement = wait.until(ExpectedConditions.
visibilityOfElementLocated(By.xpath("//a[@testid='author_search']")));


bookAuthorValue = bookAuthorElement.getText();

assertTrue(bookAuthorValue.length() > 0);


}
catch(Exception e) { }

driver.navigate().back();
}
} 


The script runs successfully but it needs 98 seconds to complete.

Is there another way for making the second script faster?

The first script proves that the site navigation between the home page, result and details pages works well.

If we agree that the navigation is not important for the second script, we can implement it not using the Selenium WebDriver framework but with the JSOUP HTTP parser library.


Implement time consuming automation scripts using the JSOUP library

Let's start cooking :)

We need the soup ingredients first: vegetables, herbs, oil ..............

Just kidding.



We will be cooking a different type of soup: JSOUP.

A few words about JSOUP (from the jsoup official site):

jsoup is a Java library for working with real-world HTML.
It provides a very convenient API for extracting and manipulating data, using the best of DOM, CSS, and jquery-like methods.

jsoup implements the WHATWG HTML5 specification, and parses HTML to the same DOM as modern browsers do.
  1. scrape and parse HTML from a URL, file, or string
  2. find and extract data, using DOM traversal or CSS selectors
  3. manipulate the HTML elements, attributes, and text
  4. clean user-submitted content against a safe white-list, to prevent XSS attacks
  5. output tidy HTML

To user JSOUP, first download the JAR file from http://jsoup.org/ and add it to the project properties.

The second script written with JSOUP looks like this:

@Test public void test1() throws IOException {

Document resultsPage = Jsoup.connect("https://vpl.bibliocommons.com/search?q=java&t=keyword").get();

Elements titles = resultsPage.select("span.title");

for (Element title : titles) {

Element link = title.child(0);

String detailsPageUrl = "https://vpl.bibliocommons.com" + link.attr("href");

Document detailsPage = Jsoup.connect(detailsPageUrl).get();

Elements bookTitle = detailsPage.getElementsByAttributeValue("testid", "text_bibtitle");

if (bookTitle.size() > 0)
assertTrue(bookTitle.get(0).text().length() > 0);

Elements bookAuthor = detailsPage.getElementsByAttributeValue("testid", "author_search");

if (bookAuthor.size() > 0)
assertTrue(bookAuthor.get(0).text().length() > 0); 

}
} 
}

Lets see what each line does:

//establishes a connection to the page;
//uses the get method to get the page content and return a document object
Document resultsPage = Jsoup.connect("https://vpl.bibliocommons.com/search?q=java&t=keyword").get();

//selects all span elements that have the title class from the document object
Elements titles = resultsPage.select("span.title");

//for each span element from the list
for (Element title : titles) {

//gets the first node of the span element; this is the title link
Element link = title.child(0);

//gets the href attribute of the a element
String detailsPageUrl = "https://vpl.bibliocommons.com" + link.attr("href");

//establishes a connection to the details page
//gets the page and returns a document object
Document detailsPage = Jsoup.connect(detailsPageUrl).get();

//finds all elements in the details page that have a testid attribute with the text_bibtitle value
Elements bookTitle = detailsPage.getElementsByAttributeValue("testid", "text_bibtitle");

//get the first found element using get(0) and its text using text()
//assert that the text length is > 0
if (bookTitle.size() > 0)
assertTrue(bookTitle.get(0).text().length() > 0);

//finds all elements in the details page that have a testid attribute with the author_search value
Elements bookAuthor = detailsPage.getElementsByAttributeValue("testid", "author_search");

//get the first found element using get(0) and its text using text()
//assert that the text length is > 0
if (bookAuthor.size() > 0)
assertTrue(bookAuthor.get(0).text().length() > 0); 

The script is simpler than the WebDriver one.

It does not work by interacting with the site through the browser.

It uses http requests instead to get the page data and then it parses or navigates through the page data.

The best part of this script is that it executes in 8 seconds!!!!

Compare this with 98 seconds needed for executing the WebDriver script.




Hope that you liked the soup recipe!

If you have any questions about the recipe, ingredients or the cook, please post them in the comments section.

The cook appreciates your interest in his recipes :)


How To Take Entire Page Screenshots In Selenium WebDriver Scripts

The ashot framework from Yandex can be used for taking screenshots in Selenium WebDriver scripts for
  1. full web pages
  2. web elements
This framework can be found on https://github.com/yandex-qatools/ashot.


Selenium WebDriver TakeScreenshot interface is rather limited


By default, screenshots can be taken in Selenium WebDriver scripts using the TakeScreenshot interface:

import static openqa.selenium.OutputType.*;
 
File screenshotFile = ((Screenshot)driver).getScreenshotAs(file);
String screenshotBase64 = ((Screenshot)driver).getScreenshotAs(base64);

If you need to take entire page screenshots or screenshots of a web element, this is possible but complicated.


Take Entire Page Screenshots With The AShot Framework



Yandex has other frameworks available in addition to Allure.

The ASHOT framework is helpful for taking screenshots.

Lets see some code samples.

The following test script implements a test case for the www.vpl.ca site that
  1. opens the home page of the site
  2. executes a keyword search
  3. clicks on the title of the first result on the results page
  4. checks that the book title is displayed on the details page
  5. checks that the book title's length is greater than 0

import static org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;

public class TestWithoutScreenshots {

WebDriver driver;

@Before
public void setUp() {
driver = new FirefoxDriver();
driver.manage().window().maximize();
}

@After
public void tearDown() {
driver.quit();
}

@Test
public void testFirstResult() throws InterruptedException
{


//opens the home page of the site
driver.get("http://www.vpl.ca");

//executes a keyword search
WebElement searchField = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchField.click();
searchField.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

Thread.sleep(3000);

//clicks on the title of the first result on the results page
WebElement searchResultLink = driver.findElement(By.xpath("(//a[@testid='bib_link'])[2]"));
searchResultLink.click();

Thread.sleep(3000);

WebElement bookTitleElement = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));
String bookTitleValue = bookTitleElement.getText();

//checks that the book title is displayed on the details page
assertEquals(bookTitleElement.isDisplayed(), true);
//checks that the book title's length is greater than 0 assertTrue(bookTitleValue.length() > 0);

}

}

The test script is part of a Maven Project.


Add The ashot Dependency To The POM.XML File

Add the following lines to the pom.xml file:

 <dependency>
<groupId>ru.yandex.qatools.ashot</groupId>
<artifactId>ashot</artifactId>
<version>1.4.12</version>
</dependency>  


Then, change the code so that entire page screenshots are taken for each page:


import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import ru.yandex.qatools.ashot.AShot;
import ru.yandex.qatools.ashot.Screenshot;
import ru.yandex.qatools.ashot.screentaker.ViewportPastingStrategy;

public class TestWithScreenshots {

WebDriver driver;

@Before
public void setUp() {
driver = new FirefoxDriver();
driver.manage().window().maximize();
}

@After
public void tearDown() {
driver.quit();
}

@Test
public void testFirstResult() throws InterruptedException, IOException
{

driver.get("http://www.vpl.ca");

//take the screenshot of the entire home page and save it to a png file
Screenshot screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\home.png"))
;

WebElement searchField = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchField.click();
searchField.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

Thread.sleep(3000);

//take the screenshot of the entire results page and save it to a png file
screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\results.png"));


WebElement searchResultLink = driver.findElement(By.xpath("(//a[@testid='bib_link'])[2]"));
searchResultLink.click();

Thread.sleep(3000);

//take the screenshot of the entire details page and save it to a png file
screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\details.png"));


WebElement bookTitleElement = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));
String bookTitleValue = bookTitleElement.getText();

assertEquals(bookTitleElement.isDisplayed(), true);
assertTrue(bookTitleValue.length() > 0);

}

}

Lets see how the screenshots look like.

The screenshot for the home page looks good:





The screenshot for the details page looks good as well:



The screenshot for the results page does not look too good:



How can we get a proper screenshot for the results page?


Take Web Element Screenshots 

The framework offers the option of getting screenshots of web elements.

See below the changed code:


import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import ru.yandex.qatools.ashot.AShot;
import ru.yandex.qatools.ashot.Screenshot;
import ru.yandex.qatools.ashot.screentaker.ViewportPastingStrategy;

public class TestWithScreenshots {

WebDriver driver;

@Before
public void setUp() {
driver = new FirefoxDriver();
driver.manage().window().maximize();
}

@After
public void tearDown() {
driver.quit();
}

@Test
public void testFirstResult() throws InterruptedException, IOException
{

driver.get("http://www.vpl.ca");

//take the screenshot of the entire home page and save it to a png file
Screenshot screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\home.png"));

WebElement searchField = driver.findElement(By.xpath("//input[@id='globalQuery']"));
searchField.click();
searchField.sendKeys("java");

WebElement searchButton = driver.findElement(By.xpath("//input[@class='search_button']"));
searchButton.click();

Thread.sleep(3000);

//take the screenshot of the entire results page and save it to a png file
screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\results.png"));

//take the screenshot of a div element that includes all results page details>br/> screenshot = new AShot().takeScreenshot(driver, driver.findElement(By.xpath("(//div[@id='ct_search'])[1]")));
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\div_element.png"));


WebElement searchResultLink = driver.findElement(By.xpath("(//a[@testid='bib_link'])[2]"));
searchResultLink.click();

Thread.sleep(3000);

//take the screenshot of the entire details page and save it to a png file
screenshot = new AShot().shootingStrategy(new ViewportPastingStrategy(1000)).takeScreenshot(driver);
ImageIO.write(screenshot.getImage(), "PNG", new File("c:\\temp\\details.png"));


WebElement bookTitleElement = driver.findElement(By.xpath("//h1[@id='item_bib_title']"));
String bookTitleValue = bookTitleElement.getText();

assertEquals(bookTitleElement.isDisplayed(), true);
assertTrue(bookTitleValue.length() > 0);

}

}


The screenshot of the div element shows the full results page:





NEXT