Wednesday, June 12, 2013

BDD using Cucumber-JVM

I personally had a tough time finding the correct list of resources, to get started with Cucumber-JVM. So, that's the sole purpose of creating this blog. It might be an added resource on the internet to help out anyone who wants to implement Behavior Driven Development and radically improve stability of their application.
A little breif about behavior driven development. It is not exactly the latest buzzword in town, however since I have used it in a lot of my previous projects, all I would like to say is that it is pretty much a very easy way to ensure that your workflows are not broken by subsequent releases. I have learnt to rely upon these tests (provided they are written well and foolproof) and so have a lot of organizations. What i have realized over time is that automate everything that you can, especially the tests and you will reach to a level of continuous delivery with your product/application. Now is that not worth the effort? So here is the hands on part.

One of the first things you would need is of-course an IDE. I prefer eclipse only because I have been using it a long time. You can write the entire set of tests using command line tools like VI also. The only thing that matters is that we configure and define paths properly to execute the tests.

The next thing you would need is the supporting jars. I assume that you would have a JDK installed and in your system/user classpath/path in order to start eclipse up, so the rest of the jars that are needed are as follows.

Here is the basic list of Jars you would need to setup your project:-

You can select your specific versions from maven repository as well.

With all the stuff needed you can start by creating a Java Project in eclipse. Once the project is created import all the jars downloaded into the projects build path and classpath (just copy all the jars in the lib directory at the project root, if not there then create one and modify the .classpath file of the project).

Start with creating the run-time interface or entry point for the test by defining a class, you can name it whatever you want, however for the sake of simplicity we will call it "RunCukesTest" in a test.java package in the source folder. We will mark it with the annotation @RunWith(Cucumber.class), it is a JUnit defined annotation that signals the dependencies to start when we run this class as a test. Here is what the class looks like.

package test.java;

import org.junit.runner.RunWith;

import cucumber.api.junit.Cucumber;


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


There are options that you can define using the @Cucumber.Options  tag at the class level e.g.

@Cucumber.Options(format="html:target/report", features="src/test/resources/" tags="@myTag")

  • format options specifies the format of the report i.e. html and the path relative to the project root where it should be saved to.
  • features define the path to the feature files, we can specify the complete relative path of a single feature file also if we only want to run it.
  • tags define the specific scenarios that you want to execute in the current run, so all the scenarios marked with @myTag will be run in the current test run
There are more options that can be configured but these are good to begin with, you can find the entire detail here.

You can right-click and run this file as a JUnit test. It will result in an error right now because you have no features defined or the required package declared.

The next step is to define a package under the source folder, call it test.resources. This will contain all your feature files. Features is a cool way of writing tests in plain English. The idea is that any one can write a feature, of-course it is to the developer to write the corresponding implementations.

The feature that you will write is a simple gmail login and open the mailbox.
Here is the feature file:- 

Feature: Gmail

Scenario: open a mail to read in gmail
Given I Login to gmail with "<username>" and "<password>"
When I click on the sign in button
Then I can see my mailbox

Save this file as GmailLogin.feature under the package test.resources

Now you will write the implementation step definitions of features. This will contain code that matches a regular expression with the annotation associated to the methods in the step definition file. For the above feature file, here is the step definition:-

package test.java;

import org.junit.Assert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

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

public class GmailStepDefs {
    WebDriver driver;

    @Before
    public void start() {
        driver = DriverHelper.getDriverInstance(DriverHelper.FIREFOX_DRIVER);
    }

    @Given("^I Login to gmail with \"([^\"]*)\" and \"([^\"]*)\"$")
    public void I_login_to_gmail(String username, String password)
            throws Exception {
        driver.get("http://mail.google.com");

        WebElement userid = driver.findElement(By.id("Email"));
        WebElement passwd = driver.findElement(By.id("Passwd"));

        userid.sendKeys(username);
        passwd.sendKeys(password);
    }

    @When("^I click on the sign in button$")
    public void I_click_on_signIn() throws Exception {
        WebElement signIn = driver.findElement(By.id("signIn"));
        signIn.click();
    }

    @Then("^I can see my mailbox$")
    public void I_can_see_my_mailBox() throws Exception {
        Thread.sleep(10000);
        WebElement composeButton = driver.findElement(By
                .xpath("//div[contains(text(), 'COMPOSE')"));
        Assert.assertEquals(true, null != composeButton);
    }

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

Save this file under the test.java package. There is a driver helper, which is a factory class which I have used to create driver instances, only because the IE Driver is a little problematic to work with and we have to ensue for the stability f the tests that we are always working with a single instance. Here is the code for it:-

package test.java;

import java.util.concurrent.TimeUnit;

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 DriverHelper {
    
    public static final String INTERNET_EXPLORER_DRIVER = "IE";
    public static final String FIREFOX_DRIVER = "FF";
    public static final String CHROME_DRIVER = "GC";
    public static WebDriver driver;
    
    public static synchronized WebDriver getDriverInstance(String driverType) {
        if (driver == null){
            if (driverType.equalsIgnoreCase(INTERNET_EXPLORER_DRIVER)) {
                System.setProperty("webdriver.ie.driver",
                        "E:\\workspace\\TestCucumber\\lib\\IEDriverServer-32bit.exe");
                DesiredCapabilities capabilities = DesiredCapabilities
                        .internetExplorer();
                capabilities
                        .setCapability(
                                InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,
                                true);
                capabilities.setCapability("ignoreProtectedModeSettings", true);
                driver = new InternetExplorerDriver(capabilities);
                driver.manage().timeouts()
                        .pageLoadTimeout(180, TimeUnit.SECONDS);
                return driver;
            } else if (driverType.equalsIgnoreCase(FIREFOX_DRIVER)){
                return new FirefoxDriver();
            } else if (driverType.equalsIgnoreCase(CHROME_DRIVER)) {
                return new ChromeDriver();
            }
        }
        return driver;
    }

}

As you can see that there is a lot of code specifically for the creation of the IE driver instance. Now go back to the RunCukesTest class right-click, select run as Junit Test and enjoy the show. You will need to have the IEDriver 32-bit  or 64-bit depending on your OS to be present in the class path. These can be downloaded here.

Please feel free to contact if you need some more help or sample code for the blog here.

No comments:

Post a Comment