Skip to content

This is a project on cucumber tests based on Java, and can be used as a resource to run a workshop on building test automation frameworks.

Notifications You must be signed in to change notification settings

5v1988/bdd-cucumber-tests

Repository files navigation

BDD CucumberJVM Workshop

Introduction

This workshop intends to provide guidance on building test automation framework, mainly using Guice, a dependency injection framework from Google. It should take from 1 to 2 hours, depending on how deep you want to go into each topic.

Workshop Outline

Requirements

Java + Gradle

Selenium

Cucumber

Guice

Scenarios

The following are two scenarios that we automate, mainly to understand the aforementioned steps

  • Create a new account
  • Search products and place an order

Step 0: Define scenarios to be automated in BDD format

Let's begin by writing steps for above two scenarios in Gherkin format. We need them as we use these to learn the concepts required in building a robust and scalable automation framework. For instance, the following steps are to be created for the first scenario: Create a new account in a feature file

@Smoke @NewAccount
Feature: Account Creation related scenarios

  Scenario Outline: Create a new account
    Given User opens home page: url
    When User navigates to create new customer account
    And User enters auto-generated personal info
    And User checks the checkbox 'Sign Up for Newsletter'
    And User enters following sign-in info:
      | email           |  |
      | password        |  |
      | confirmPassword |  |
    Then User creates an account and verifies the message: "<message>"

    Examples:
      | message                                            |
      | Thank you for registering with Main Website Store. |

All the features being used in this project can be viewed from this folder: features For further reference on as to how to write scenarios on Gherkin, you can follow my blog post on Gherkin on medium

Step 1: Create a custom module and an injector source

Firstly, create a class that extends Guice's AbstractModule, where in we define methods that provide all necessary objects while injecting in step definition classes, and also page classes.

import com.google.inject.AbstractModule;
public final class FrameworkModule extends AbstractModule {
  
}

In CucumberInjectorSource class, there are two modules that are created via injectors — the first one is Cucumber related which comes from cucumber-guice library. This mainly ensures that all the page objects created are all on scenario scope, and are destroyed once the scenario ends.

In addition to Cucumber module, we also pass our own module FrameworkModule that we defined in the previous step to the injector source that takes care of creating all dependent objects and injecting them where needed, mostly on step definition and page classes.

public class CucumberInjectorSource implements InjectorSource {

  @Override
  public Injector getInjector() {
    return Guice.createInjector(Stage.PRODUCTION, CucumberModules.createScenarioModule(),
        new FrameworkModule());
  }
}

Step 2: Create a method that provides test configurations

Now that module is defined , let's begin by adding a provider within a module that create and returns an object to hold all test configuration like application url, browser to be used etc. This is a simple POJO class, created using the values from a file in yaml format. Note that scope here, which is @Singleton, meaning we will use the same singleton object throughout a test run.

  @Provides
  @Singleton
  TestConfig providesTestConfiguration() {
    Yaml yaml = new Yaml();
    InputStream inputStream = this.getClass().getClassLoader()
        .getResourceAsStream("config/test-config.yml");
    TestConfig testConfig = yaml.loadAs(inputStream, TestConfig.class);
    return testConfig;
  }

Step 3: Create a method that provides TestContext

In this step, we are just adding another provider TestContext which is plainly to share states between steps within the same class and between the step definition classes. It stores values using key and value. Also note that scope is @ScenarioScoped which will ensure that TestContext object is be recreated for every scenario.

  @Provides
  @ScenarioScoped
  TestContext<String, Object> providesTextContext() {
    return new TestContext<>();
  }

Step 4: Create a provider that returns a standard Faker object

In this, one of the providers in FrameworkModule returns Faker object which will be injected into the class, preferably in the step definition classes where we need randomly generated data, which will be used in our tests. Faker object will be in @Singleton scope.

  @Provides
  @Singleton
  Faker providesFakerInstance() {
    return new Faker();
  }

Step 5: Create a provider that returns a WebDriver

This is a provider that returns WebDriver instance, for a given browser type. It's also worth to note that we also inject TestConfig object in the parameter,created in the previous step that takes care of providing the value for browser type. In this case, it is designated as @ScenarioScoped as I intend to use a new browser instance for each scenario in the suite.

  @Provides
  @ScenarioScoped
  @Inject
  WebDriver providesBrowserInstance(TestConfig testConfig) {
    String browser = Optional.ofNullable(testConfig.getBrowser()).get();
    switch (browser) {
      case "firefox":
        return WebDriverManager.firefoxdriver().create();
      case "edge":
        return WebDriverManager.edgedriver().create();
      default:
        return WebDriverManager.chromedriver().create();
    }
  }

Putting all these providers together, this is the view of FrameworkModule class: FrameworkModule

Step 6: Create pages with required elements and methods:

There are several page classes that we may have to create in reality, however for the sake of simplicity, let's consider this class ReviewAndPaymentsPage that is implemented for Review & Payments page in our test app. If you notice that this class is scenario scoped, as mentioned previously, here we are instantiating a page object, keeping it throughout the scenario, and discarding it once the scenario is done, irrespective of the test status.

The next thing to note here is that, we are injecting WebDriver object in the page constructor parameter which is required to initialize the page elements using PageFactory design.

Lastly, we declared a page element placeOrderBtn and implemented a method placeOrder which is to click and place an order.

The same pattern can be followed while implementing other pages on the testing app.

@ScenarioScoped
public class ReviewAndPaymentsPage extends BasePage {

  @Inject
  public ReviewAndPaymentsPage(WebDriver driver) {
    super(driver);
    PageFactory.initElements(driver, this);
  }
  
  @FindBy(how = How.CSS, using = "button[title='Place Order']")
  private WebElement placeOrderBtn;

  public void placeOrder() {
    waitUntil(placeOrderBtn);
    pause(Duration.ofSeconds(3));
    placeOrderBtn.click();
  }

}

For the testing app, I used for this workshop, all the pages are defined within this package here:pages

Step 7: Define glue codes for the steps in feature files:

This is simple step definition class that contains an implementation for a step User reviews payments and places order. Since it's required ReviewAndPaymentsPage object to place an order, we inject it using Guice through the module we defined on the first step. This way, we are allowed to inject other objects like Faker, TestContext, TestConfig etc. in similar way.

public class ReviewAndPaymentsSteps {

  @Inject
  ReviewAndPaymentsPage reviewAndPaymentsPage;

  @Then("User reviews payments and places order")
  public void userReviewsPaymentsAndPlacesOrder() {
    reviewAndPaymentsPage.placeOrder();
  }
  
}

Likewise, all the glue codes are defined within this package here: glue

Step 8: Run tests with a simple configuration:

 gradle test -Dcucumber.filter.tags="@NewAccount"

Godspeed!

Veera.

About

This is a project on cucumber tests based on Java, and can be used as a resource to run a workshop on building test automation frameworks.

Resources

Stars

Watchers

Forks

Packages

No packages published