Acceptance testing with Cucumber and Selenium(Java)

In this post, I am going to show how we can write the acceptance test script with Cucumber and Selenium.

Refer my earlier post for Ruby/Cucumber Acceptance testing with Cucumber and Capybara

My objective is to write an acceptance test which opens up the browser and goes to ‘Yelp’ site and search for a restaurant and validate the results.

Refer the below feature file.
java-cucumber-selenium-test/src/test/resources/yelp/test/yelp.feature



Feature: Search Restaurants

  Scenario: Go to yelp and search for valid restaurant
    Given a user goes to Yelp
    When Search for taco bell
    Then See the List of taco bell Restaurants and confirm results contains Lower Greenville

  Scenario: Go to yelp and search for restaurant
    Given a user goes to Yelp
    When Search for Qboba
    Then See the List of Qboba Restaurants

  Scenario: Go to yelp and search for restaurant
    Given a user goes to Yelp
    When Search for Chipotle
    Then See the List of Chipotle Restaurants

  Scenario: Go to yelp and search for invalid restaurant
    Given a user goes to Yelp
    When Search for hhahsdahsdhasd
    Then See No Results found error message

  Scenario Outline:Go to yelp and search for <searchText>
    Given a user goes to Yelp
    When Search for <searchText>
    Then See the List of  Restaurants
    Examples:
      | searchText               |
      | Scardello                |
      | Roti Grill               |
      | Mughlai Restaurant       |
      | Spice In The City Dallas |


Refer the below Junit test file
java-cucumber-selenium-test/src/test/java/yelp/test/YelpTest.java



package yelp.test;

import org.junit.runner.RunWith;
import cucumber.api.junit.Cucumber;

@RunWith(Cucumber.class)
public class YelpTest {
}

Refer the below Step definitions file.

java-cucumber-selenium-test/src/test/java/yelp/test/definitions/YelpStepDefinition.java



package yelp.test.definitions;

import cucumber.api.java.After;
import cucumber.api.java.Before;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertEquals;

public class YelpStepDefinition {

    protected WebDriver driver;

    @Before
    public void setup() {
        driver = new FirefoxDriver();
        //Loading the driver
        System.setProperty("webdriver.gecko.driver", "/usr/local/bin/geckodriver");
    }

    @Given("^a user goes to Yelp$")
    public void goToYelp() {
        //Get the Yelp home page
        driver.get("https://www.yelp.com/dallas");
    }

    @When("^Search for (.*?)$")
    public void doSearch(String searchTerm) throws InterruptedException {
        WebElement searchDescElement = driver.findElement(By.id("find_desc"));
        searchDescElement.sendKeys(searchTerm);

        WebElement submit = driver.findElement(By.id("header-search-submit"));
        submit.submit();
    }

    @Then("^See the List of (.*?) Restaurants$")
    public void verifyContents(String restaurant) {

        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
        assertEquals(true, isContentAvailable(restaurant));
    }

    @Then("^See the List of (.*?) Restaurants and confirm results contains (.*?)$")
    public void verifyContentsWithLandmark(String restaurant, String landMark) {
        //Create an Explicit wait to make sure that the page is loaded and driver updates the page data.
        WebDriverWait myWait = new WebDriverWait(driver, 10);
        myWait.until(ExpectedConditions.visibilityOfElementLocated(By.id("yelp_main_body")));
        assertEquals(true, isContentAvailable(restaurant, landMark));
    }

    public boolean isContentAvailable(String... contents) {
        WebElement searchResultsElement = driver.findElement(By.id("yelp_main_body"));
        boolean result = false;
        for (String content : contents) {
            result = searchResultsElement.getText().contains(content);
        }
        return result;
    }

    @Then("^See No Results found error message")
    public void verifyContents() {
        //Create an Explicit wait to make sure that the page is loaded and driver updates the page data.
        WebDriverWait myWait = new WebDriverWait(driver, 10);
        myWait.until(ExpectedConditions.visibilityOfElementLocated(By.id("yelp_main_body")));
        assertEquals(true, driver.getPageSource().contains("No Results"));
    }

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


Follow the below steps to run the test script

1. Download the repository
2. Download the ‘gecko’ driver from https://github.com/mozilla/geckodriver/releases and copy it into ‘/usr/local/bin/’
3. Do ‘mvn clean install’
4. Maven builds the test class and runs it. We can also run the test case(YelpTest.java) from any IDE as normal Junit test case.

Mock System class with Mockito + PowerMock

In this post, I am going to show how to mock the System.getenv and System.getProperty methods.

I have used Mockito and PowerMock to do this. Make sure to include the below dependencies in your pom file.


      <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito</artifactId>
            <version>1.6.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>1.6.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito-common</artifactId>
            <version>1.6.5</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-core</artifactId>
            <version>1.6.4</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.powermock.tests</groupId>
            <artifactId>powermock-tests-utils</artifactId>
            <version>1.6.4</version>
            <scope>test</scope>
        </dependency>
 

I have created an another util class which has the getEnv and getProperty methods. So instead of mocking the System class methods explicitly, I have mocked the MockUtils static methods.

MockUtils.java


package com;

import org.apache.commons.lang3.StringUtils;

public class MockUtils {

    public static String getEnv(String name) {
        return StringUtils.defaultIfBlank(System.getenv(name), "");
    }

    public static String getProperty(String name) {
        return StringUtils.defaultIfBlank(System.getProperty(name), "");
    }
}

Here is my test class.

MockTest.java



package com;

import com.MockUtils;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static junit.framework.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MockUtils.class)
@PowerMockIgnore("javax.management.*")
public class MockTest {

    @BeforeClass
    public static void setupClass() throws Exception {
        PowerMockito.mockStatic(MockUtils.class);
        PowerMockito.when(MockUtils.getEnv("APP_ENV")).thenReturn("DEV");
        PowerMockito.when(MockUtils.getProperty("username")).thenReturn("bala");
    }

    @Test
    public void testMockUtils() {
        assertEquals("DEV", MockUtils.getEnv("APP_ENV"));
        assertEquals("bala", MockUtils.getProperty("username"));
    }
	

Sometimes the static method might be used in the underlying classes and if those classes are not using the mocked values, then you can initialize the mocked values in the @Before like below,


import static junit.framework.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest(MockUtils.class)
@PowerMockIgnore("javax.management.*")
public class MockTest {

    @Before
    public void setupClass() throws Exception {
        PowerMockito.mockStatic(MockUtils.class);
        PowerMockito.when(MockUtils.getEnv("APP_ENV")).thenReturn("DEV");
        PowerMockito.when(MockUtils.getProperty("username")).thenReturn("bala");
    }

    @Test
    public void testMockUtils() {
        assertEquals("DEV", MockUtils.getEnv("APP_ENV"));
        assertEquals("bala", MockUtils.getProperty("username"));
    }
	

Build Operate Check – Acceptance Pattern

Build operate Check is one of the acceptance pattern. So its good that to follow this pattern while creating the Junit test cases.

The first step is to build the input data and hold it in memory, then execute it on the actual code which means call/invoke the code to operate on that data. Finally check the results of that operation.

Please refer the below example.


package com.dao.test;

import org.junit.Test;
import static org.junit.Assert.assertNotNull;
import com.dao.ContactDAO;
import com.model.Contact;

public class ContactDAOTest{
    @Test
    public void testCreateContactData() throws Exception {
        //Build the test data
        Contact contact = buildContactData("bala","dublin");
        //Call the ContactDAO to create the contact information. Assume that 
        //Create() returns an id after the successful creation of the contact in DB
        String id = new ContactDAO().create(contact);
        //check the id and make sure that its not null/blank
        assertNotNull(id);
    }

    private Contact buildContactData(String name, String city) {
        Contact contact = new Contact();
        contact.setName(name);
        contact.setCity(city);
        return contact;
    }
}



package com.model;
public static class Contact {

        private String id;
        private String name;
        private String city;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getCity() {
            return city;
        }

        public void setCity(String city) {
            this.city = city;
        }
    }