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.

Advertisements

TypeScript

TypeScript is a typed superset of Javascript that compiles to plain Javascript. Due to its static typing, the code is easier to understand and debug it. Due to modules, namespaces and OOP support, it’s useful for very large and complicated apps. Angular 2 framework is written in TypeScript.

TypeScript code needs to be compiled into Javascript so it’s easier for us to find the errors in the compilation step itself.

Follow the steps mentioned in this link to install the TypeScript Typescript installationTypescript installation

Here is the simple typescript code which contains all the important concepts.

tsMain.ts


//Class
class Greet {
    constructor(){
      console.log("Greet constructor is called") 
    } 
    greet(name: string):void { 
      console.log("Hello"+name) 
   } 
} 

var obj = new Greet(); 
obj.greet("Bala");

//Inheritance
class Vehicle {
    name: string;
    constructor(name: string) { this.name = name; }
    display(noOfWheels: number = 0) {
        console.log("Vehicle Name: "+this.name+" has " +noOfWheels +" Wheels");
    }
}

class Bike extends Vehicle {
    constructor(name: string) { super(name); }
    display(noOfWheels = 2) {
        super.display(noOfWheels);
    }
}

class Car extends Vehicle {
    constructor(name: string) { super(name); }
    display(noOfWheels = 4) {
        super.display(noOfWheels);
    }
}


let bike = new Bike("Pulsar");
let car = new Car("Honda Accord");

bike.display(2);
car.display(4);



//Abstract Class

abstract class Employee {
    name: string;
    earnings: number;

    constructor(name: string) { this.name = name; }
    abstract calculatePay(): void;    
}

class HourlyContractor extends Employee {
    hoursWorked: number;
    constructor(name: string, hoursWorked: number) { super(name); this.hoursWorked=hoursWorked; }

    calculatePay() {
          this.earnings = 70*this.hoursWorked;
          console.log(this.name + "Earnings:"+this.earnings);
    }
}

class FullTimeEmployee extends Employee {
    hoursWorked: number;
    constructor(name: string, hoursWorked: number) { super(name); this.hoursWorked=hoursWorked; }

    calculatePay() {
          this.earnings = 80*this.hoursWorked;
          console.log(this.name + "Earnings:"+this.earnings);
    }
}

let bob = new HourlyContractor("Bob",30);
let john = new FullTimeEmployee("John",40);

bob.calculatePay();
john.calculatePay();



Follow the below steps to compile and run the Typescript script


1. Compile the typescript with tsc command.It will generate a js file with the same name in the current path.
$tsc tsMain.ts 

2. Then run the generated js file with node as below,

$node tsMain.js
Greet constructor is called
HelloBala
Vehicle Name:Pulsar has 2 Wheels
Vehicle Name:Honda Accord has 4 Wheels
BobEarnings:2100
JohnEarnings:3200

How to use Alex collins docker maven plugin

The “alexec/docker-maven-plugin” is a maven plugin which is used to build, test and publishing the docker images. Refer this link for more information about this plugin(https://github.com/alexec/docker-maven-plugin).

Check out my sample project @ https://github.com/dkbalachandar/java-rest-docker. This is a simple Hello world REST application running on Docker.

In this post, I am going to show how to use “alexec” docker maven plugin with an example. Let’s go into the details one by one.

1. The first step is to add the com.alexecollins.docker plugin in the build section of the pom.xml. Check below snippet.

java-rest-docker/pom.xml:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com</groupId>
    <artifactId>hello-rest</artifactId>
    <version>1.0</version>
    <properties>
       <docker.image.name>hello-rest</docker.image.name>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>com.Grizzly</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.Grizzly</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <!-- Add the Alex Collins plugin-->
            <plugin>
                <groupId>com.alexecollins.docker</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>2.11.21</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>clean</goal>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.grizzly</groupId>
            <artifactId>grizzly-http-server</artifactId>
            <version>2.3.22</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.grizzly</groupId>
            <artifactId>grizzly-http-servlet</artifactId>
            <version>2.3.24</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-servlet</artifactId>
            <version>2.5</version>
        </dependency>
    </dependencies>
</project>

2. Next step is to create a folder named ‘docker’ under src/main. Then go into ‘docker’ folder and create an another folder named ‘helloRest’. You can give name any name you want.

3. Next step is to create a conf.yml and give all the configuration information in it. Refer the below snippet.

java-rest-docker/src/main/docker/helloRest/conf.yml:


packaging:
  add:
    - target/${project.build.finalName}-jar-with-dependencies.jar
    - src/main/bin
tags:
    - ${project.artifactId}:latest

I am using the project.artifactId as my image name(hello-rest). If your project has any upper case letter then this plugin wont work. Make sure to give lower case letter always.

Assume that, the project artifactId is “hello-Rest” and when you try to build the project, you will get the below error.


[ERROR] Error during callback
com.alexecollins.docker.orchestration.OrchestrationException: com.github.dockerjava.api.exception.InternalServerErrorException: Error parsing reference: "hello-Rest:latest" is not a valid repository/tag

	at com.alexecollins.docker.orchestration.DockerOrchestrator.build(DockerOrchestrator.java:395)
	at com.alexecollins.docker.orchestration.DockerOrchestrator.build(DockerOrchestrator.java:323)
	at com.alexecollins.docker.orchestration.DockerOrchestrator.build(DockerOrchestrator.java:855)
	at com.alexecollins.docker.mojo.PackageMojo.doExecute(PackageMojo.java:16)
	at com.alexecollins.docker.mojo.AbstractDockerMojo.execute(AbstractDockerMojo.java:164)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:209)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:84)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:59)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBuild(LifecycleStarter.java:183)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:161)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: com.github.dockerjava.api.exception.InternalServerErrorException: Error parsing reference: "hello-Rest:latest" is not a valid repository/tag



4. Next step is to create the DockerFile. Check the below snippet which has all the information.

java-rest-docker/src/main/docker/helloRest/Dockerfile



FROM java:8

WORKDIR /opt

ADD hello-rest-${project.version}-jar-with-dependencies.jar /opt/helloRest.jar
ADD bin /opt/bin

RUN chmod +x /opt/helloRest.jar
RUN chmod +x /opt/bin/run.sh

CMD ["/opt/bin/run.sh"]

EXPOSE 8080


5. Run the ‘mvn clean’ command and check the build log and confirm that the image ‘hello-rest’ is built with the configuration information from conf.yml and Dockerfile.
6. Run the ‘docker images’ command to confirm the image availability.
7. Finally, Run the docker image with this command ‘docker run -p 8080:8080 hello-rest’ and test it with this URL http://localhost:8080/api/greeting.

SimpleDateFormat – ISO8601 format

I have done some date format operations in Java and got to know about the Zulu time which is nothing but the UTC time.

Now, I have a date and want to format that into “yyyy-MM-dd’T’HH:mm:ss.SSSZ” format. Here Z stands for zulu timezone.

Refer the below program and the output of those.


import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;


public class Main {

    public static void main(String[] args) {

        //ISO8601 format
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

        //EST time
        Calendar calendar1 = Calendar.getInstance();
        System.out.println("calendar1::" + calendar1.getTimeZone());
        System.out.println("\n");
        System.out.println("calendar1.getTime()::" + dateFormat.format(calendar1.getTime()));
        System.out.println("\n");
        //PDT time
        Calendar calendar2 = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
        dateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
        System.out.println("calendar2::" + calendar2.getTimeZone());
        System.out.println("calendar2.getTime()::" + dateFormat.format(calendar2.getTime()));

    }
}


calendar1::sun.util.calendar.ZoneInfo[id="America/New_York",offset=-18000000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/New_York,offset=-18000000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]


calendar1.getTime()::2017-06-02T17:03:04.643-0400


calendar2::sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
calendar2.getTime()::2017-06-02T14:03:04.645-0700

calendar1 uses the default timezone and in this case its EST time. So when i am doing the format, the output string is “2017-06-02T16:53:24.122-0400”. Here -0400 represents the time difference between the current timezone(EST) and UTC(Zulu) time.

calendar2 uses the PDT time and also set the same timezone to SimpleDateFormat as well. So the output string is “2017-06-02T13:53:24.124-0700”. Here the -0700 represents the time difference between the PDT timezone and UTC(Zulu) time.