Thursday, August 2, 2018

Parameterization in Cucumber - Bean Objects

Another way to pass data table is through data table transformer to convert the data table to a list of bean object. You don't need to define data entry transformer in the previous version of Cucumber 3.0. But you have to do that if you use the latest version of Cucumber.

Here is the feature definition.

  Scenario: Login Failure
    Given User is on Home Page
    And Sign In link is displayed
    When User clicks the Sign In link
    And User enters Credentials to login
    | Username | Password|
    | username | password|
    | username1 | password1|
    Then User should be login failed message

And Bean definition.

package me.simplejavautomation.data;

public class Credentials {

    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}


Define and register table transformer.

package me.simplejavautomation.data.transformer;

import java.util.Locale;
import java.util.Map;

import cucumber.api.TypeRegistry;
import cucumber.api.TypeRegistryConfigurer;
import io.cucumber.datatable.DataTableType;
import io.cucumber.datatable.TableEntryTransformer;
import me.simplejavautomation.data.Credentials;

public class TypeRegistryConfiguration implements TypeRegistryConfigurer {

    @Override
    public Locale locale() {
        return Locale.ENGLISH;
    }

    @Override
    public void configureTypeRegistry(TypeRegistry typeRegistry) {
        typeRegistry.defineDataTableType(new DataTableType(Credentials.class,
                  new TableEntryTransformer<Credentials>() {

            @Override
            public Credentials transform(Map<String, String> entry)
                   throws Throwable {
                Credentials credentials = new Credentials();
                credentials.setUsername(entry.get("Username"));
                credentials.setPassword(entry.get("Password"));
                return credentials;
            }
        }));
    }
}


Add transformer package to CucumberOptions configuration

package me.simplejavautomation;

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/features",
       glue = { "me.simplejavautomation.stepdefs",
        "me.simplejavautomation.data.transformer" })
public class CucumberTestRunner {

}

And then you are ready to go.

Parameterization in Cucumber - Data Tables

To pass a group of data to a single feature step, you can use Data Tables in Cucumber feature definition.

Scenario: Successful Login with Valid Credentials
    Given User is on Home Page
    When User Navigate to LogIn Page
    And User enters Credentials to LogIn
        | user1 | p2ssword |
        | user2 | p2ssword |
    Then Message displayed Login Successfully

Just use "|" mark to separate different column and each line as a table row.

@When("^User enters Credentials to LogIn$")
public void user_enters_credentials_to_login(DataTable credentials) {
    List<List<String>> data = credentials.asLists();
    driver.findElement(By.id("xv_username")).sendKeys(data.get(0).get(0));
    driver.findElement(By.id("header-myairnz-password")).sendKeys(data.get(0).get(1));
    driver.findElement(By.cssSelector("input[name='login']")).click();
}

When it is passed to step definition method at runtime, you may access this data table through DataTable parameter.


1) Data tables as List<List<String>>
     You can call "credentials.asLists();" to get a List<List<String>> object to access all the data in the data table, just like a two dimension array.

2) Data tables as List<Map<String, String>>
    Or you can call "credentials.asMaps() to get a List<Map<String, String>> object to access all the data in the data table, but make sure you have added a key row in the first row in the data table.

     | Username| Password|
     | user1 | p2ssword |
     | user2 | p2ssword |

    To access Map list,

List<Map<String, String>> data =  dataTable.asMaps();
username = data.get(0).get("Username");
password = data.get(0).get("Password");

Check the other interface in DataTable, you can find other ways you wanted.

Wednesday, August 1, 2018

Parameterization in Cucumber - Examples

Another way to pass test input to step definition method is using Examples keyword in Scenario Outline.


  Feature: Sign In

  Scenario Outline: Login Failure
    Given User is on Home Page
    And Sign In link is displayed
    When User clicks the Sign In link
    And User enters wrong "<username>" and "<password>"
    Then User should be login failed message

  Examples:
      | username  | password  |
      | username1 | password1 |
      | username2 | password2 |


The Cucumber runner will execute all the inputs in Examples table one by one in while scenario steps.

The step definition is the same as the last post.

Parameterization in Cucumber - Quotation

When feature step has been glued to step definition, there are several ways to pass parameters to step definition from features.

The first way is using quotation marks ("") in features.

  Feature: Sign In

  Scenario: Login Failure
    Given User is on Home Page
    And Sign In link is displayed
    When User clicks the Sign In link
    And User enters wrong "username" and "password"
    Then User should see the failed message


So the "username" and "password" will be passed to step definition method as parameters.

@When("User enters wrong \"(.*)\" and \"(.*)\"")
public void user_enters_wrong_username_and_password(String username, String password) {
...
}

Cucumber Options

To execute the Cucumber test, you have to configure features and glue to Cucumber runner.

package me.simplejavautomation;

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/features",
    glue = { "me.simplejavautomation.stepdefs"})
public class CucumberTestRunner {

}

Here is the diagram of CucumberOptions annotation.

dryRun -- show steps if don't have step definition.
features -- the paths of feature files.
glue -- the classpath of step definition files.
tags -- specify which tags in feature files should be executed.
monochrome -- display console output in the much readable way.
plugin -- specify different formatting options for the output reports.
strict -- if strict mode is enabled (fail if there are undefined or pending steps).
name -- specify a pattern filter for features or scenarios
snippets -- what format should the snippets use. underscore, camelcase.
junit -- the options for the JUnit runner.

Tuesday, July 31, 2018

About Gherkin Keywords

Gherkin is a structured natural language that is used by business analysts to specify how they want the system to behave for given scenarios. The Gherkin language is simple.  It uses about 10 keywords (Given, When, Then, And, But, Scenario, Feature, Background, Scenario Outline, Examples) which allow the language to be read and parsed by an automation tool called Cucumber. Here is an example:

Feature: Some terse yet descriptive text of what is desired
  In order to realize a named business value
  As an explicit system actor
  I want to gain some beneficial outcome which furthers the goal

  Scenario: Some determinable business situation
    Given some precondition
      And some other precondition
     When some action by the actor
      And some other action
      And yet another action
     Then some testable outcome is achieved
      And something else we can check happens too


Gherkin has a few high-level benefits:

1) Business Friendly Language: Gherkin is a simple to understand language using a limited number of keywords.  It's simplicity and natural style make it easy for business people to read and understand.

2) Requirement Traceability: It's used to write acceptance tests with a focus on system behavior.  Each acceptance test links to a scenario and feature which allows it to be traced back to the original requirements.  This is particularly evident with Agile methods that use short user stories combined with acceptance tests to define the requirements.

3) Test Automation: As a structured language using keywords, Gherkins is machine readable with the automation tool called Cucumber.  Developers write executable code for each step of the Gherkin acceptance test resulting in fully automated test scenarios.

4) Team Communication: With such a strong focus on defining acceptance tests business analyst, QA, and developers find themselves working more closely together to define, build, and test their applications.  The Gherkin acceptance test becomes a powerful and precise communication tool.

Here is the list of keywords that Gherkin supports:

  • Feature - List of scenarios.
  • Background - List of steps run before each of the scenarios
  • Scenario - Business rules through the list of steps with arguments.
  • Given - Some precondition step
  • When - Some key actions
  • Then - To observe outcomes or validation
  • And - To enumerate more Given, When, Then steps
  • But - To enumerate more Given, When, Then steps
  • Examples - Container for s table
  • * - replacement of all other step/blue keys

First Example of Cucumber

This is a simple example of showing how the Cucumber framework works.

To run Cucumber test, we need to define a Cucumber runner using @RunWith annotation.

package me.simplejavautomation;

import org.junit.runner.RunWith;

import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/features",
    glue = { "me.simplejavautomation.stepdefs" })
public class CucumberTestRunner {

}

Cucumber.class is derived from JUnit runner. There are two attributes you need to define in CucumberOptions. One is 'features' to let Cucumber runner know where features are, it's the directory path of features. Another one is 'glue' to let Cucumber know where your Step Definitions are, it's the java package.

Second is to define features using Gherkin. It's a text file with .feature as the extension, and store in your features directory.

  Feature: Sign In

  Scenario: Login Failure
    Given User is on Home Page
    And Sign In link is displayed
    When User clicks the Sign In link
    And User enters wrong "username" and "password"
    Then User should see the failed message


The third is to define the step definition which is the glue between features and Java functions.

package me.simplejavautomation.stepdefs;

import static org.junit.Assert.assertEquals;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

public class SignInSteps {

    private WebDriver driver;

    @Given("User is on Home Page")
    public void user_is_on_Home_Page() {
        driver = new ChromeDriver();
        driver.get("https://www.airnewzealand.co.nz/");
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    }

    @Given("Sign In link is displayed")
    public void sign_In_link_is_displayed() {

    }

    @When("User clicks the Sign In link")
    public void user_clicks_the_Sign_In_link() {
        driver.findElement(By.id("login-button")).click();
    }

    @When("User enters wrong username\"(.*)\" and password\"(.*)\"")
    public void user_enters_wrong_username_and_password(String username, String password) {
        driver.findElement(By.id("xv_username")).sendKeys(username);
        driver.findElement(By.id("header-myairnz-password")).sendKeys(password);
        driver.findElement(By.cssSelector("input[name='login']")).click();
    }

    @Then("User should see the failed message")
    public void user_should_see_the_failed_message() {
        String alertMessage = driver.findElement(By.cssSelector("div[class='airnz-Alert__message']"))
                .findElement(By.xpath(".//p")).getText();
        assertEquals("To sign in to our website you now need to be ...", alertMessage);
    }

}

After you have done this, just simply execute JUnit Test under CucumberTestRunner class in your eclipse. You will see that it will follow the steps defined in features and execute them one by one.

Monday, July 30, 2018

Configure Cucumber using Gradle in Eclipse

To configure Cucumber using Gradle in Ecilpse, you may follow the same steps in Configure Selenium WebDriver using Gradle in Eclipse. The only thing you need to do is to add cucumber dependency in build.gradle file.

To get the latest cucumber dependency, you may check the Cucumber website here or search.maven.org by searching 'cucumber-java'. The latest version is 3.0.2.

And copy cucumber gradle config to build.gradle file.

dependencies {
    // testCompile 'io.cucumber:cucumber-java8:3.0.2'
    testCompile 'io.cucumber:cucumber-java:3.0.2'
    testCompile 'io.cucumber:cucumber-junit:3.0.2'
}

Wednesday, July 25, 2018

Testing Report using Gradle

After we execute all the test cases using Gradle "test" task, it will generate HTML reports for us, it shows how many tests failed and how many tests passed.

There are two ways to execute the Gradle "test" task.

1) Run Gradle test in eclipse

After finish running all test cases, it looks like this,


2) Execute "gradle test" in command line under the project directory

3) Open the report HTML file in the browser

Tuesday, July 24, 2018

Capture Screenshot when Test Failed

Capture screenshot if the test case failed is an essential feature for investigating the defects. Selenium WebDriver implemented TakesScreenshot interface which is easy to take a screenshot from web browser screen. Here is an example:

public class Screenshot {

    public static void takeScreenshot(String screenshotPath, WebDriver driver) {
        try {
            File screenshotFile =
                  ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
            FileUtils.copyFile(screenshotFile, new File(screenshotPath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void takeScreenshot(WebDriver driver, String method) {
        String screenshotName = DateUtils.formatDate(new Date(), "yyyyMMddHHmmss_")
                   + method + ".jpg";
        String dateString = DateUtils.formatDate(new Date(), "yyyyMMdd");
        File dir = new File("screenshots/" + dateString);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String screenshotPath = dir.getAbsolutePath() + "/" + screenshotName;
        takeScreenshot(screenshotPath, driver);
    }

}

JUnit Test Case:

@RunWith(SimpleRunner.class)
public class ScreenshotTest {

    public static WebDriver driver;

    @BeforeClass
    public static void setUpBeforeClass() {
        driver = new ChromeDriver();
    }

    @AfterClass
    public static void tearDownAfterClass() {
        driver.quit();
    }

    @Test
    public void testPercentageCalculation() {
        // open http://www.percentagecalculator.co/ web site
        driver.get("http://www.percentagecalculator.co/");
        // find A element and type 10
        driver.findElement(By.id("A")).sendKeys("10");
        // find B element and type 100
        driver.findElement(By.id("B")).sendKeys("100");
        // find C element
        WebElement c = driver.findElement(By.id("C"));
        // validate result
        assertEquals("11", c.getAttribute("value"));
    }

}

JUnit Runner:

public class SimpleRunner extends BlockJUnit4ClassRunner {

    public SimpleRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    public void run(RunNotifier notifier) {
        notifier.addListener(new JUnitExecutionListener());
        notifier.fireTestRunStarted(getDescription());
        super.run(notifier);
    }

}

RunListener:

public class JUnitExecutionListener extends RunListener {

    public void testRunStarted(Description description) throws Exception {
    }

    public void testRunFinished(Result result) throws Exception {
    }

    public void testStarted(Description description) throws Exception {
    }

    public void testFinished(Description description) throws Exception {
    }

    public void testFailure(Failure failure) throws Exception {
        Screenshot.takeScreenshot(ScreenshotTest.driver,
            failure.getDescription().getMethodName());
    }

    public void testAssumptionFailure(Failure failure) {
        Screenshot.takeScreenshot(ScreenshotTest.driver,
            failure.getDescription().getMethodName());
    }

    public void testIgnored(Description description) throws Exception {
    }

}

Monday, July 23, 2018

Setup Selenium Grid

With Selenium Grid you can create a simple infrastructure of various browsers on different operating systems to not only distribute test load but also give you a diversity of browsers to work with.

 1) Download Selenium Standalone Server
     You man download Selenium standalone server jar file from here. And put the jar file in your disk folder.

2) Start Hub Machine
     Open command line window and navigate to the folder of jar file and run the command below.
     java -jar selenium-server-standalone-3.13.0.jar -role hub
     The hub will be running on default port 4444, you may access the link http://localhost:4444 to check the hub status.

3) Start Node Machine
     Navigate to the folder of jar file on the node machine in command line window, and run the command below.
     java -jar selenium-server-standalone-3.3.1.jar -role node -hub   
            http://<hub>:4444/grid/register -port 5555
     It will start a node on port 5555 and register on the hub. Then you may find it on the hub machine status page.

4) Connect to Remote Driver
     DesiredCapabilities cap = DesiredCapabilities.firefox();
     WebDriver driver = new RemoteWebDriver(new URL("http://<hub>:4444/wd/hub"), cap);

Here is an example test case.

package me.simplejavautomation;

import static org.junit.Assert.assertEquals;

import java.net.URL;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.PageFactory;

import me.simplejavautomation.pages.PercentageCalculatorPage;

public class GridTest {

    private WebDriver driver;

    @Before
    public void setUp() throws Exception {
        String hub = "http://192.168.1.61:4444/wd/hub";
        DesiredCapabilities cap = DesiredCapabilities.firefox();
        driver = new RemoteWebDriver(new URL(hub), cap);
    }

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

    @Test
    public void testPercentageCalculation() {
        // open http://www.percentagecalculator.co/ web site
        driver.get("http://www.percentagecalculator.co/");

        PercentageCalculatorPage pageObject = PageFactory.initElements(driver,
             PercentageCalculatorPage.class);
        pageObject.getA().sendKeys("10");
        pageObject.getB().sendKeys("100");

        assertEquals("10", pageObject.getC().getAttribute("value"));
    }

}

Friday, July 20, 2018

PageObejctModel in Selenium WebDriver

Page object model is a wonderful design pattern to abstract out Web Page elements and its actions from actual tests. We can use this way to build a test framework of the project and focus on business only in all the test cases.

Here is a demo for page object model.


package me.simplejavautomation.pages;

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;

public class PercentageCalculatorPage {

    @FindBy(id = "A")
    private WebElement a;

    @FindBy(id = "B")
    private WebElement b;

    @FindBy(id = "C")
    private WebElement c;

    public WebElement getA() {
        return a;
    }

    public void setA(WebElement a) {
        this.a = a;
    }

    public WebElement getB() {
        return b;
    }

    public void setB(WebElement b) {
        this.b = b;
    }

    public WebElement getC() {
        return c;
    }

    public void setC(WebElement c) {
        this.c = c;
    }

}

package me.simplejavautomation;

import static org.junit.Assert.assertEquals;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.PageFactory;

import me.simplejavautomation.pages.PercentageCalculatorPage;

public class PageObjectTest {

    private WebDriver driver;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
    }

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

    @Test
    public void testPercentageCalculation() {
        // open http://www.percentagecalculator.co/ web site
        driver.get("http://www.percentagecalculator.co/");

        PercentageCalculatorPage pageObject = PageFactory
            .initElements(driver, PercentageCalculatorPage.class);
        pageObject.getA().sendKeys("10");
        pageObject.getB().sendKeys("100");

        assertEquals("10", pageObject.getC().getAttribute("value"));
    }

}

Thursday, July 19, 2018

Execute Javascript in Selenium WebDriver

To execute Javascript using Selenium WebDriver, we can use JavascriptExecutor interface to implement it. There are two methods in JavascriptExecutor interface.

  • executeScript(String, Object...)
  • executeAsyncScript(String, Object...)

Here is a simple test,


    @Test
    public void testJavascript() throws InterruptedException {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
        jsExecutor.executeScript("alert('hello world');");
        TimeUnit.SECONDS.sleep(4);
        driver.switchTo().alert().accept();

        WebElement continents = (WebElement) jsExecutor
            .executeScript("return document.getElementById('continents')");
        assertNotNull(continents);

        String selectedContinent = jsExecutor
             .executeScript("return document.getElementById('continents').value")
             .toString();
        assertEquals("Asia", selectedContinent);
    }

Tuesday, July 17, 2018

Actions on WebElement in Selenium WebDriver

Actions is an interaction generator in Selenium WebDriver which allow us to operate complicated user interactions on web pages.
You can perform actions such as drag and drop, move, click, double-click, and tick and so on.

Here is an example of a demo of drag and drop, and move on a menu.

package me.simplejavautomation;

import static org.junit.Assert.assertEquals;

import java.util.concurrent.TimeUnit;

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.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;

public class ActionsApiTest {

    private WebDriver driver;
    private String url;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
        url = "http://simplejavautomation.blogspot.com/2018/07/demo-page-for-actions-in-selenium.html";
        // go to the demo page
        driver.get(url);
        // maximize the browser window
        driver.manage().window().maximize();
    }

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

    @Test
    public void testDragAndDrop() throws InterruptedException {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        WebElement from = driver.findElement(By.id("draggable"));
        WebElement to = driver.findElement(By.id("droppable"));

        Actions builder = new Actions(driver);
        // builder.clickAndHold(from).release(to).build().perform();
        builder.dragAndDrop(from, to).build().perform();
    }

    @Test
    public void testMenuClick() throws InterruptedException {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        WebElement automation = driver.findElement(By.id("automation"));

        Actions builder = new Actions(driver);
        builder.moveToElement(automation).build().perform();

        driver.findElement(By.id("selenium")).click();

        assertEquals("Selenium",
             driver.findElement(By.id("selectedText")).getText());
    }

}

Demo Page for Actions in Selenium WebDriver

jQuery UI Droppable - Default functionality
 

Drag me to my target

Drop here

Monday, July 16, 2018

Using switchTo to switch Window, Alert, Frame

 Knowing how to deal with Windows, Alerts, and Frames are essential in Selenium WebDriver.

In Selenium WebDriver, it uses TargetLocator interface to handle this switch. You may use it to switch to,

  • Frame (using index, id or name, WebElement)
  • Alert
  • Window (using window handle)

Uses "driver.switchTo()" to get TargetLocator, and make sure using "driver.switchTo().defaultContent()" to switch back to main window.

Here is an example to demonstrate all these cases.


package me.simplejavautomation;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class SwitchApiTest {

    private WebDriver driver;
    private String url;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
        url = "http://simplejavautomation.blogspot.com/2018/07/demo-page-for-selenium-webdriver-apis.html";
        // go to the demo page
        driver.get(url);
        // maximize the browser window
        driver.manage().window().maximize();
    }

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

    @Test
    public void testSwitchWindow() {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);

        String currentWindow = driver.getWindowHandle();
        // find element
        driver.findElement(By.linkText("Selenium")).click();

        assertEquals(currentWindow, driver.getWindowHandle());

        String[] windowHandles = driver.getWindowHandles().toArray(new String[0]);
        assertTrue(windowHandles.length > 1);

        driver.switchTo().window(windowHandles[1]);
        assertEquals(windowHandles[1], driver.getWindowHandle());
    }

    @Test
    public void testSwitchAlert() {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        // find element
        driver.findElement(By.id("simpleAlert")).click();

        Alert alert = driver.switchTo().alert();
        assertEquals("Simple Java Automation", alert.getText());
        alert.accept();

        driver.findElement(By.id("confirmAlert")).click();

        alert = driver.switchTo().alert();
        assertEquals("Simple Java Automation", alert.getText());
        alert.dismiss();

        driver.findElement(By.id("promptAlert")).click();

        alert = driver.switchTo().alert();
        assertEquals("Is Java Automation simple?", alert.getText());
        alert.sendKeys("Yes");
        alert.accept();
    }

    @Test
    public void testSwitchFrame() {
        JavascriptExecutor exe = (JavascriptExecutor) driver;
        int numberOfFrames = Integer.parseInt(exe.executeScript("return window.length").toString());
        assertEquals(6, numberOfFrames);

        // By finding all the web elements using iframe tag
        List<WebElement> iframeElements = driver.findElements(By.tagName("iframe"));
        assertEquals(6, iframeElements.size());

        // switch frame by index
        driver.switchTo().frame(0);
        // switch back
        driver.switchTo().defaultContent();
        // switch frame by id
        driver.switchTo().frame("iframeA");

        driver.switchTo().defaultContent();

        WebElement iframeB = driver.findElement(By.id("iframeB"));
        // switch frame by web element
        driver.switchTo().frame(iframeB);
        driver.switchTo().defaultContent();
    }

    @Test
    public void testSwitchFrame2() {
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        // switch frame by id
        driver.switchTo().frame("iframeA");
        driver.findElement(By.id("street")).sendKeys("Whangaparaoa Rd");
        driver.findElement(By.id("suburb")).sendKeys("Red Beach");
        driver.findElement(By.id("submit")).click();

        Alert alert = driver.switchTo().alert();
        assertEquals("Street:Whangaparaoa Rd Suburb:Red Beach", alert.getText());
        alert.accept();

        // switch back
        driver.switchTo().defaultContent();

        // switch frame by index
        driver.switchTo().frame("iframeB");
        driver.findElement(By.id("city")).sendKeys("Auckland");
        driver.findElement(By.id("country")).sendKeys("New Zealand");
        driver.findElement(By.id("submit")).click();

        alert = driver.switchTo().alert();
        assertEquals("City:Auckland Country:New Zealand", alert.getText());
        alert.accept();
     
        driver.switchTo().defaultContent();
    }

}

Wait APIs in Selenium WebDriver

Sometimes we need to wait until a WebElement present so that we can do further test steps. Here are common Waits in Selenium WebDriver APIs.

  • Thread sleep 
  • Implicitly Wait 
  • FluentWait 
  • WebDriverWait 
  • AjaxWait (depends on how slow the ajax call, probably not the stable one)

Here is the sample test case.

package me.simplejavautomation;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Select;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

public class WaitApiTest {

    private WebDriver driver;
    private String url;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
        url = "http://simplejavautomation.blogspot.com/2018/07/demo-page-for-selenium-webdriver-apis.html";
        // go to the demo page
        driver.get(url);
        // maximize the browser window
        driver.manage().window().maximize();
    }

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

    @Test(expected = NoSuchElementException.class)
    public void testImplicitlyWaitWithException() {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);

        // find element
        WebElement waitLink = driver.findElement(By.linkText("WebDriver Wait"));
        assertNotNull(waitLink);
    }

    @Test
    public void testImplicitlyWait() {
        // wait 10 seconds if web element is not present
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

        // find element
        WebElement waitLink = driver.findElement(By.linkText("WebDriver Wait"));
        assertNotNull(waitLink);
    }

    @Test
    public void testFluentWait() {
        Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(Duration.ofSeconds(10))
                .pollingEvery(Duration.ofSeconds(1)).ignoring(NoSuchElementException.class);
        // find element
        WebElement waitLink = wait.until(new Function<WebDriver, WebElement>() {

            @Override
            public WebElement apply(WebDriver driver) {
                return driver.findElement(By.linkText("WebDriver Wait"));
            }

        });
        assertNotNull(waitLink);
    }

    @Test
    public void testWebDriverWait() {
        WebDriverWait wait = new WebDriverWait(driver, 10);

        // find element
        WebElement waitLink = wait.until(ExpectedConditions.elementToBeClickable(By.linkText("WebDriver Wait")));
        assertNotNull(waitLink);
    }

    @Test
    public void testHtmlWait() {
        driver.manage().timeouts().pageLoadTimeout(100, TimeUnit.SECONDS);
        driver.manage().timeouts().setScriptTimeout(100, TimeUnit.SECONDS);
    }

    @Test
    public void testAjaxWait() {
        Select citySelect = new Select(driver.findElement(By.id("cities")));
        citySelect.selectByVisibleText("London");

        driver.findElement(By.id("weather")).click();

        // wait ajax finish
        WebDriverWait wait = new WebDriverWait(driver, 10);
        wait.until(new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver t) {
                Boolean isJqueryCallDone = (Boolean) ((JavascriptExecutor) driver)
                        .executeScript("return jQuery.active==0");
                return isJqueryCallDone;
            }
        });

        // weather info retrieved
        WebElement weatherInfo = wait.until(new Function<WebDriver, WebElement>() {
            @Override
            public WebElement apply(WebDriver t) {
                WebElement info = driver.findElement(By.id("weatherInfo"));
                if (info != null) {
                    if (info.getText().startsWith("City:")) {
                        return info;
                    }
                    return null;
                }
                return info;
            }
        });
        assertTrue(weatherInfo.getText().startsWith("City: London"));
    }

}

Sunday, July 15, 2018

Getting to know about Selenium WebDriver APIs

Before writing the first case, we should know about Selenium WebDriver APIs. The most commonly used APIs are WebDriver, WebElement, Select, Navigation, Options, Window, Timeouts.

Here is a diagram to give you a general idea.
WebElement and Select.
You can find detail information for those interfaces through selenium java docs here, and documentation here.

We will create a simple test case to show you how to operate different web elements, such as text box, radio button, checkbox, select, multiple select, button, and table.

Before looking into the code, take few minutes to look at the demo site here.

package me.simplejavautomation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.List;
import java.util.concurrent.TimeUnit;

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.chrome.ChromeDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.Select;

public class WebDriverApiTest {

    private WebDriver driver;
    private String url;

    @Before
    public void setUp() {
        driver = new ChromeDriver();
        url = "http://simplejavautomation.blogspot.com/2018/07/demo-page-for-selenium-webdriver-apis.html";
    }

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

    @Test
    public void testWebDriverApis() {
        driver.get(url);
        String expectedTitle = "Simple Java Automation: Demo Page for Selenium WebDriver APIs";
        String actualTitle = driver.getTitle();
        assertEquals(expectedTitle, actualTitle);

        String actualUrl = driver.getCurrentUrl();
        assertEquals(url, actualUrl);

        String pageSource = driver.getPageSource();
        assertTrue(pageSource.length() > 0);

        driver.findElement(By.linkText("Simple Java Automation")).click();

        driver.navigate().back();
        driver.navigate().forward();

        String expectedUrl = "http://simplejavautomation.blogspot.com/";
        actualUrl = driver.getCurrentUrl();
        assertEquals(expectedUrl, actualUrl);

        driver.navigate().to(url);
        driver.navigate().refresh();
        actualUrl = driver.getCurrentUrl();
        assertEquals(url, actualUrl);
    }

    @Test
    public void testWebElementApis() {
        // go to the demo page
        driver.get(url);
        // maximize the browser window
        driver.manage().window().maximize();

        // get FirstName, LastName, and Email input and send keys to them
        driver.findElement(By.name("firstname")).sendKeys("Tristan");
        driver.findElement(By.name("lastname")).sendKeys("Zhou");
        driver.findElement(By.name("email")).sendKeys("jiahuan.zhou@yahoo.com");

        // set Male radio button checked
        List sexRadios = driver.findElements(By.name("sex"));
        boolean isMaleSelected = sexRadios.get(0).isSelected();
        if (!isMaleSelected) {
            sexRadios.get(0).click();
        }

        // set experience to 5+
        WebElement expRadio = driver.findElement(By.id("exp-6"));
        expRadio.click();

        // set Automation Tester checked
        List professionChecks = driver.findElements(By.name("profession"));
        for (int i = 0; i < professionChecks.size(); i++) {
            String checkValue = professionChecks.get(i).getAttribute("value");
            if (checkValue.equalsIgnoreCase("Automation Tester")) {
                professionChecks.get(i).click();
                break;
            }
        }

        // check all Automation Tools
        driver.findElement(By.id("tool-0")).click();
        driver.findElement(By.xpath("//*[@value='Selenium IDE']")).click();
        driver.findElement(By.cssSelector("input[value='Selenium Webdriver']")).click();

        // choose Australia for Continents select box
        Select continentSelect = new Select(driver.findElement(By.id("continents")));
        continentSelect.selectByIndex(2);
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
        continentSelect.selectByVisibleText("Australia");

        // select Browser and Navigation option
        Select commandSelect = new Select(driver.findElement(By.name("selenium_commands")));
        commandSelect.selectByIndex(0);
        driver.manage().timeouts().implicitlyWait(2, TimeUnit.SECONDS);
        commandSelect.selectByVisibleText("Navigation");

        // click submit button
        driver.findElement(By.id("submit")).click();

        // validate all inputs which displays in the table
        assertEquals("Tristan", getCellValue(2));
        assertEquals("Zhou", getCellValue(3));
        assertEquals("jiahuan.zhou@yahoo.com", getCellValue(4));
        assertEquals("Male", getCellValue(5));
        assertEquals("5+", getCellValue(6));
        assertEquals("Automation Tester", getCellValue(7));
        assertEquals("QTP, Selenium IDE, Selenium Webdriver", getCellValue(8));
        assertEquals("Australia", getCellValue(9));
        assertEquals("Browser, Navigation", getCellValue(10));

        // move to the table element
        new Actions(driver).moveToElement(driver.findElement(By.id("summary"))).build().perform();

        // sleep 5 seconds
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Get 2nd cell value for the row.
     * 
     * @param row
     * @return
     */
    private String getCellValue(int row) {
        return driver.findElement(By.xpath("//*[@id='summary']/tbody/tr[" + row + "]/td[2]")).getText();
    }

}

WebElement Locators and XPath

To access web element on the page, you have to know the id, or name, or path to locate them. There are several different ways to find them. For example,

  • Browser's built-in Inspector
  • FireBug & FirePath addon in Firefox Browser
  • WebDriver Element Locator addon in Firefox Browser
  • XPath Helper in Chrome

You may find the handiest tool for yourself while using them.

There are 8 explicit locators: id, name, identifier, dom, xpath, link, css and ui, but you don't need everything while writing the test case.

Here are some examples,

id: driver.findElement(By.id("username"));
namedriver.findElement(By.name("username"));
xpathdriver.findElement(By.xpath("//*[@id='username']"));
linkdriver.findElement(By.linkText("Link Text"));
cssdriver.findElement(By.cssSelector("input[id='username']"));

What is XPath
XPath is a language that describes a way to locate and process items in Extensible Markup Language (XML) documents by using an addressing syntax based on a path through the document’s logical structure or hierarchy.

There are two types of XPath,
Absolute xpath, start from root element: /html/body/div/div/section/div/a
Relative xpath, start from located element: //*[@id=’footer’]/div/a

More examples of XPath,
//img[contains(@src,’Profile’)]
//img[starts-with(@alt,’Visit Us On Linkedin’)]
//*[text()='Simple Java Automation']
//a[contains(text(), 'Java Automation')]
//*[@id=’username’]
//input[@id=’username’]
//form[@name=’loginForm’]/input
//*[@name=’loginForm’]/input

Saturday, July 14, 2018

Demo Page for Selenium WebDriver APIs

This is a demo page that we will use in future test cases.

 
 
 
First name:
 
Last name:
 
Email:
 
  Male   
 
0    1    2    3    4    5   
 
Manual Tester   
 
  QTP   Selenium IDE   Selenium Webdriver
 

 
 

 

 

ItemValue
First name
Last name
Email
Sex
Years of Experience
Profession
Automation Tool
Continents 
Selenium Commands
 

Friday, July 13, 2018

First test case using Selenium WebDriver

After setup Gradle project and web driver configuration, it's ready to write the first test case using Selenium WebDriver.

In this article, we are going to test a percentage calculator on the site Percentage Calculator. Here is the page looks like.


There are three text boxes on the page, if you type 10 and 100 in two blue text boxes, you will get 10 in the third text box, which means 10% of 100 equals 10.

Now we are creating the test case using JUnit in Eclipse.

1) Create a package
     Right click on "src/test/java" source folder, and choose New > Package.

    Type a package name in "Name" field and click the "Finish" button.

2) Create a Test Case
    Right click on "me.simplejavautomation" package, and choose New > JUnit Test Case.

    Type a class name in "Name" field and click the "Finish" button. And make sure to check the "setUp()" and "tearDown()" check-boxes.

3) Write Test Case
     Type the code below and will let you familiar with the selenium classes and interfaces.

4) Run the Test Case
    Right-click the Test Class or test method and choose Run As > JUnit Test. A new browser window will be opened and you will see,
    a) It navigates to the website http://www.percentagecalculator.co/
    b) The cursor moves on first text-box and types 10
    c) The cursor moves on second text-box and types 100
    d) The third text-box value changes to 10
    e) The browser is closed
    Then, you will get the green bar in the JUnit tab which shows you the test case passed.

5) How to identify the element on the page?
    Before we can find an element on the page using Web Driver, we need to identify the id, name, CSS or XPath on the element. Follow the steps below to find the right element id.
    a) Open the website using Google Chrome
    b) Press F12 or Ctrl+Shift+I to open Developer Tools
    c) Click the arrow button on the top left of Developer Tools window or Ctrl+Shif+C
    d) Point your mouse to the element you want on the page
    You may find the id, name, CSS on the web element and Properties tab on the right side. In this example, the first textbox's id is A. Then you can find ids for other two textboxes.

6) Run Test Case on other browsers
    To run the test case on different browsers, just instantiate the related Web Driver object in "setUp()" function.

Prerequisite for the First Test Case using Selenium WebDriver

Using Selenium WebDriver, we can test web applications against different browsers, such as Google Chrome, Mozilla Firefox, Internet Explorer and so on. You can find all supported browsers from here.

Before we start writing our first test case, there are several things we need to set up. We are going to use the latest version 3.13.0.

1) Download Web Drivers for each browser.
     In this article, we are going to test on three browsers, Google Chrome, Mozilla Firefox, and Internet Explorer. You can find web drivers from selenium website here. And download web driver zip file for each browser. And unzip all files into a folder in your machine.
    There should be three executable files in the folder after finished download, chromedriver.exe, geckodriver.exe, and IEDriverServer.exe.

2) Set property in Environment Variables
     In order to start web driver successfully, we need to enable these web drivers. The first option is to setup system environment variables in your machine.
     Open the "Environment Variables" from the Control Panel and add a new item into "Path", in this example the path is "D:\selenium-drivers", which is the location of web driver files.

      The second option is to config library path through System.setProperty function in the test case.
       System.setProperty("webdriver.chrome.driver", "Path to chromedriver.exe");
       System.setProperty("webdriver.gecko.driver", "Path to geckodriver.exe");
       System.setProperty("webdriver.ie.driver", "Path to IEDriverServer.exe");

       After we have done this, selenium should be able to find the web driver.

3) Enable protected mode for Internet Explorer
    You may find NoSuchElement exception when executing the test case on Internet Explorer. That probably you haven't enable protected mode in your IE settings.
     Open "Security" tab in "Internet Options", and check the Enable Protected Mode check-box. And make sure you have done this for all zones.

4) Using Win32 web driver for Internet Explorer
     You may find it slow to send a value to the input element when executing the test case on Internet Explorer. Try to download Win32 web driver.


Thursday, July 12, 2018

Configure Selenium WebDriver using Gradle in Eclipse

Gradle is an extensive build tool and dependency manager which allow you to manage project dependencies SUPER EASY.
To configure Selenium WebDriver using Gradle in Eclipse, we need to perform the following steps:
1) Start your Eclipse IDE by double click on ‘eclipse.exe‘ in eclipse installation folder.
    And create a new Gradle Project from File > New > Project.

2) Choose Gradle > Gradle Project option, and click 'Next'. 


3) Click 'Next' on the Gradle Welcome page.


4) Give a Project name, for example, 'SeleniumGradle' and click 'Finish' button.


5) Open the Gradle configuration file 'build.gradle'. There is the 'dependencies' section for adding project dependencies. You can search dependencies from The Central Repository


6) Let's search for 'selenium-java' to find selenium libraries.


7) The latest version of selenium is 3.13.0 which updated on 25-Jun-2018.


8) Click the version link '3.13.0' to the detail page.


9) Copy the code line under 'Gradle/Grails' section. 


10) Paste the code into 'build.gradle' file under 'dependencies' section.


11) Then, right-click the project and select the Gradle > Refresh Gradle Project to refresh the dependencies in the project.


12) After refresh project completed, you may find all selenium libraries have been imported into the project. 


Then you are ready to add automation test now.