automatictester

my thoughts on test automation

Basic CucumberJVM + Selenium WebDriver test automation framework

Below you can find complete, working test automation framework, which can be used to support BDD in your organisation. It bundles together CucumberJVM and Selenium WebDriver, with TestNG behind the scenes. Key features and design concerns:

  • Build with Maven
  • TestNG is used to enable parallel execution on runner level
  • Feature and runner is duplicated, to demonstrate tests (i.e. runners) can run in parallel
  • Every new feature must be associated with runner to run with mvn test
  • Cucumber features are imperative (in real world this probably will not be the case)
  • Framework design is oversimplified, to focus solely on CucumberJVM/Selenium/TestNG integration
  • Advanced configurability is not implemented (see above)
  • Reporting is not fine tuned

pom.xml

<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/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>uk.co.automatictester</groupId>
    <artifactId>jwebfwk</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>jwebfwk</name>

    <build>

        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.18.1</version>
                <configuration>
                    <suiteXmlFiles>
                        <suiteXmlFile>src/test/resources/testng/testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                </configuration>
            </plugin>

        </plugins>

    </build>

    <dependencies>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>2.46.0</version>
        </dependency>

        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.8.21</version>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-testng</artifactId>
            <version>1.2.2</version>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>1.1.7</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>1.3</version>
        </dependency>

    </dependencies>

</project>

src/test/resources/testng/testng.xml

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Test runner" parallel="classes" thread-count="2">
    <test name="Package with subpackages">
        <packages>
            <package name="uk.co.automatictester.jwebfwk.runners.*"/>
        </packages>
    </test>
</suite>

src/test/resources/features/Download.feature

Feature: Download section
  In order to use Selenium in my project, I want to download Selenium language bindings

  Scenario: Java binding download link check
    Given I am on the Selenium homepage
    When I click "Download" tab
    Then I should see "Java" download link on Download page
    And I should see "C#" download link on Download page
    And I should see "Ruby" download link on Download page
    And I should see "Python" download link on Download page
    And I should see "Javascript (Node)" download link on Download page

src/test/resources/features/Download_Copy.feature

Feature: Download section
  In order to use Selenium in my project, I want to download Selenium language bindings

  Scenario: Java binding download link check
    Given I am on the Selenium homepage
    When I click "Download" tab
    Then I should see "Java" download link on Download page
    And I should see "C#" download link on Download page
    And I should see "Ruby" download link on Download page
    And I should see "Python" download link on Download page
    And I should see "Javascript (Node)" download link on Download page

src/test/java/uk/co/automatictester/jwebfwk/runners/DownloadFeatureRunner.java

package uk.co.automatictester.jwebfwk.runners;

import cucumber.api.CucumberOptions;
import cucumber.api.testng.AbstractTestNGCucumberTests;

@CucumberOptions(features = "src/test/resources/features/Download.feature",
        glue = "uk.co.automatictester.jwebfwk.glue",
        format = {"pretty"})
public class DownloadFeatureRunner extends AbstractTestNGCucumberTests {
}

src/test/java/uk/co/automatictester/jwebfwk/runners/DownloadFeatureRunner_Copy.java

package uk.co.automatictester.jwebfwk.runners;

import cucumber.api.CucumberOptions;
import cucumber.api.testng.AbstractTestNGCucumberTests;

@CucumberOptions(features = "src/test/resources/features/Download_Copy.feature",
        glue = "uk.co.automatictester.jwebfwk.glue",
        format = {"pretty"})
public class DownloadFeatureRunner_Copy extends AbstractTestNGCucumberTests {
}

src/test/java/uk/co/automatictester/jwebfwk/page/objects/DownloadPage.java

package uk.co.automatictester.jwebfwk.page.objects;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import uk.co.automatictester.jwebfwk.framework.ParentPage;

public class DownloadPage extends ParentPage {

    private static final String DOWNLOAD_LINK = "//div[@id='mainContent']//table[1]//tbody//td[text()='%s']//..//td[4]//a[text()='Download']";

    public DownloadPage(WebDriver driver) {
        super(driver);
    }

    public boolean hasDownloadLinkFor(String linkText) {
        By downloadLinkLocator = By.xpath(String.format(DOWNLOAD_LINK, linkText));
        return hasElement(downloadLinkLocator);
    }

}

src/test/java/uk/co/automatictester/jwebfwk/page/objects/MainPage.java

package uk.co.automatictester.jwebfwk.page.objects;

import org.openqa.selenium.WebDriver;
import uk.co.automatictester.jwebfwk.framework.ParentPage;

public class MainPage extends ParentPage {

    public MainPage(WebDriver driver) {
        super(driver);
    }

    public void clickTab(String tab) {
        click(tab);
    }

}

src/test/java/uk/co/automatictester/jwebfwk/glue/StepDefinitions.java

package uk.co.automatictester.jwebfwk.glue;

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 uk.co.automatictester.jwebfwk.framework.ParentScenario;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;

public class StepDefinitions extends ParentScenario {

    @Before
    public void beforeScenario() {
        startBrowser();
    }

    @Given("^I am on the Selenium homepage$")
    public void I_am_on_the_Selenium_homepage() {
        navigateTo();
    }

    @When("^I click \"([^\"]*)\" tab$")
    public void I_click_tab_on(String tab) {
        mainPage.clickTab(tab);
    }

    @Then("^I should see \"([^\"]*)\" download link on Download page$")
    public void I_should_see_download_link_on_download_page(String linkText) {
        assertThat(downloadPage.hasDownloadLinkFor(linkText), is(true));
    }

    @After
    public void afterScenario() {
        closeBrowser();
    }
}

src/test/java/uk/co/automatictester/jwebfwk/framework/ParentScenario.java

package uk.co.automatictester.jwebfwk.framework;

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import uk.co.automatictester.jwebfwk.page.objects.DownloadPage;
import uk.co.automatictester.jwebfwk.page.objects.MainPage;

import java.util.concurrent.TimeUnit;

public class ParentScenario {

    private WebDriver driver;

    protected DownloadPage downloadPage;
    protected MainPage mainPage;

    protected void startBrowser() {

        driver = new FirefoxDriver();
        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

        downloadPage = new DownloadPage(driver);
        mainPage = new MainPage(driver);
    }

    protected void navigateTo() {
        driver.navigate().to("http://docs.seleniumhq.org/");
    }

    protected void closeBrowser() {
        driver.quit();
    }

}

src/test/java/uk/co/automatictester/jwebfwk/framework/ParentPage.java

package uk.co.automatictester.jwebfwk.framework;

import org.openqa.selenium.WebDriver;

public abstract class ParentPage extends DSL {

    public ParentPage(WebDriver driver) {
        super(driver);
    }

}

src/test/java/uk/co/automatictester/jwebfwk/framework/DSL.java

package uk.co.automatictester.jwebfwk.framework;

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

public abstract class DSL {

    private WebDriver driver;

    public DSL(WebDriver driver) {
        this.driver = driver;
    }

    public void click(String text) {
        click(By.linkText(text));
    }

    public void click(By by) {
        driver.findElement(by).click();
    }

    public boolean hasElement(By by) {
        return !driver.findElements(by).isEmpty();
    }

}

Your mvn test output should be similar to:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running TestSuite
Feature: Download section
In order to use Selenium in my project, I want to download Selenium language bindings
Feature: Download section
In order to use Selenium in my project, I want to download Selenium language bindings
Starting ChromeDriver (v2.9.248307) on port 46253
Starting ChromeDriver (v2.9.248307) on port 7664

Scenario: Java binding download link check                            # src/test/resources/features/Download.feature:4
Given I am on the Selenium homepage                                 # StepDefinitions.I_am_on_the_Selenium_homepage()
When I click “Download” tab                                         # StepDefinitions.I_click_tab_on(String)
Then I should see “Java” download link on Download page             # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “C#” download link on Download page                # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Ruby” download link on Download page              # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Python” download link on Download page            # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Javascript (Node)” download link on Download page # StepDefinitions.I_should_see_download_link_on_download_page(String)

1 Scenarios (1 passed)
7 Steps (7 passed)
0m6.882s

Scenario: Java binding download link check                            # src/test/resources/features/Download_Copy.feature:4
Given I am on the Selenium homepage                                 # StepDefinitions.I_am_on_the_Selenium_homepage()
When I click “Download” tab                                         # StepDefinitions.I_click_tab_on(String)
Then I should see “Java” download link on Download page             # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “C#” download link on Download page                # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Ruby” download link on Download page              # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Python” download link on Download page            # StepDefinitions.I_should_see_download_link_on_download_page(String)
And I should see “Javascript (Node)” download link on Download page # StepDefinitions.I_should_see_download_link_on_download_page(String)

1 Scenarios (1 passed)
7 Steps (7 passed)
0m7.340s

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.189 sec - in TestSuite

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.780 s
[INFO] Finished at: 2015-06-11T12:03:30+02:00
[INFO] Final Memory: 7M/81M
[INFO] ------------------------------------------------------------------------

Advertisements

16 responses to “Basic CucumberJVM + Selenium WebDriver test automation framework

  1. Vikas August 10, 2015 at 3:30 pm

    Hi, excellent post. I’m able to run the scripts as per your example.

    Now I’ve one more requirement. can you explain me how to read values from parameters tag of testng.xml. See below example:

    I’ve to read BrowserName and Environment values from parameters tag. I’ve tried to use @parameters for @Before method of cucumber but it didn’t work out and gave exception that @Before hook only accepts one parameter that too of type scenario. can you explain me how to read values from parameters tag.

    Bit urgent, thanks in advance.

  2. Shanti September 16, 2015 at 9:50 am

    I have created the project setup on eclipse, but I couldn’t find TestNg anywhere when I right click on the DownloadFeatureRunner.java. I have included the TestNg dependency on pom.xml and also on the build path. Pls help me out.

  3. kirann November 26, 2015 at 5:24 am

    This is excellent! IT worked like a charm for me Thanks a ton!

  4. Kiran December 13, 2015 at 6:56 am

    Hi ,

    First of all thanks for the excellent post which gave me some relief in the current situatuion I had .I have a TestNG run cucumber framework and I had setup framework just duplicating runner file but not feature files.The same set of features file are being user by both RunCukesTest.java and CopofRunCukesTest.java .I have put enormous effort to get parallel execution where I’m running into issues where the test behaves wierd. With the above example, Can I use my framework with duplicates of Runner as well as Features? Please take some time to respond. Im in a trouble now and need to convey this to management that i would be using duplicate Features & RunCucumber.java file

    I would try your implemented approach with my framework and hope it goes well

    Thanks in Advance
    Kiran

    • automatictester December 13, 2015 at 12:47 pm

      I’m happy to help, but to do so I need detailed information what’s going wrong. I don’t know what you mean by “test behaves wierd”.
      Yes, you can have multiple runners. Yes, every runner can be associated with one or more features.
      Parallel execution on runner level is possible, and this has been described in detail in this post.
      Just make sure your framework is thread-safe and you don’t use statics or singletons where you shouldn’t.

  5. Kiran December 13, 2015 at 9:39 am

    In Adddition to my above query Hope i can associate multiple features to a Single Test Runner

    Thanks,
    Kiran

    • automatictester December 13, 2015 at 12:39 pm

      Yes you can. Instead of passing a single String:
      features = “src/test/resources/features/A.feature”
      You can pass an array of Strings:
      features = {“src/test/resources/features/A.feature”, “src/test/resources/features/B.feature”}

  6. nilesh1234 December 23, 2015 at 1:07 pm

    Hi I am not able to generate the result using mvn test as shown in your post.I just get the below result.

    SeleniumFramework\jwebfwk>mvn test
    [INFO] Scanning for projects…
    [INFO]
    [INFO] ——————————————————————-
    [INFO] Building jwebfwk 1.0-SNAPSHOT
    [INFO] ——————————————————————-
    [INFO] ——————————————————————-
    [INFO] BUILD SUCCESS
    [INFO] ——————————————————————-
    [INFO] Total time: 0.117 s
    [INFO] Finished at: 2015-12-23T17:29:25+05:30
    [INFO] Final Memory: 5M/107M
    [INFO] ——————————————————————-

    I have created the complete clone of your steps that you defined in the post.Please let me know what steps I missed….

    • automatictester December 24, 2015 at 8:14 pm

      I just double-checked and everything works fine for me when I re-created project from scratch using code snippets publised here. I’d recommend to copy & paste everything again, or at least check if pom.xml is correct.

  7. Vitaliy Laputko December 25, 2015 at 3:11 pm

    Hi,

    thanks a lot for you post)
    and can you pls show your class AbstractTestNGCucumberTests
    Thanks)

    • automatictester December 26, 2015 at 12:01 am

      It is not my class – it’s defined in package cucumber.api.testng which is pulled in as a dependency.

    • Raghav Joshi February 9, 2016 at 10:59 am

      Hello Vitaliy,
      You just need to add the following dependency to your pom.xml and then you can import “import cucumber.api.testng.AbstractTestNGCucumberTests;” and then you can extend AbstractTestNGCucumberTests to your runner class.

      info.cukes
      cucumber-testng
      1.1.5
      test

      There is no need to create own AbstractTestNGCucumberTests class.

      Hope this will sort out your problem.

  8. GR April 22, 2016 at 2:27 pm

    This is great .. it helped be got up and running quickly.
    I was wondering if we could define testng groups at the runner level ? I know we can define it at the super class that extends IHookable but that will take away the flexibility to create custom groups at the individual test level.

  9. cooldiv4u June 2, 2016 at 11:25 am

    Hi, I would like to know what happens when we have multiple step definition files, and for one scenario i am calling methods from these multiple step definition files then how will the @Before and @After methods for starting and stoping the browser instance work? the above example will only work in case where a feature calls method from single step definition file right??

  10. Nilesh July 21, 2016 at 2:44 pm

    HI ,
    Can you please let me know how to skip the test cases in cucumber-testng framework

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: