Hot questions for Using Cucumber in hook

Question:

I want to extend from a "AbstractBase_step" class in java. So I want to have a hook like:

public abstract class AbstractBase_Steps {
    protected Scenario scenario;
    @Before
    public void background(Scenario scenario) {
        this.scenario = scenario;
    }
}

which is called for every step file:

public abstract class Hello_Steps extends AbstractBase_Steps {
}

When I do this I get

cucumber.runtime.CucumberException: You're not allowed to extend classes that define Step Definitions or hooks. class Hello_Steps extends class AbstractBase_Steps

Does somebody has a trick for that?

EDIT: For reusing same step definitions I've created a new Class Common_Steps and included it in the glue path. So its definitions are available for all feature files in the test.


Answer:

Check this discussion.

Hope this Helps.

Question:

In Jbehave we can execute method upon outcome of scenario success/failure. One of the below mentioned methods will execute after failure and success.

@AfterScenario(uponOutcome=AfterScenario.Outcome.SUCCESS)
public void afterSuccessfulScenario() {
// Some code to execute...
}

@AfterScenario(uponOutcome=AfterScenario.Outcome.FAILURE)
public void afterFailedScenario() {
// Some code to execute...
}

I'm trying to achieve the same thing in Cucumber-jvm. The scenario Hooks @Before and @After will execute after each scenario. But I want to execute some code for each failure scenario.

I am not sure how to implement it in cucumber-jvm. Any clue?


Answer:

You can achieve it using cucumber-jvm hooks. If a hook receive a Scenario as a parameter, you can know if it has failed or not.

Example:

@After
public void afterScenario(Scenario scenario) {
  if (scenario.isFailed()) {
     // Some code to execute...
  }
  if(scenario.isPassed()) {
     // Some code to execute...
  }
}

Note the import of @After:

import cucumber.api.java.After;

Hope it helps.

Question:

My challenge is that I have two different types of tests that run using Cucumber BDD with Java, Maven, and JUnit.

In several features, related to the UI, I need to perform some actions before every single scenario such as spinning up VMs, like this:

public class StepDefinitions {
    @Before
    protected void setUp(Scenario scenario) throws MalformedURLException {
        //Create browser resources here for all of my UI related scenarios
} 

However, in non-UI tests, such as API tests, I don't need those browsers to be spun up. Hence, I really need a different behavior for the @Before method called setUp.

The challenge that I am facing is that it seems as though the @Before hook works for every single test method, even if these methods are in different classes. As a result, no matter what I try, the browser resources are always created, even for API tests that don't need the browsers.

Here is what I've tried without success:

  • I created a totally separate feature file and StepDefinitions file for the API tests. The definitions file has no reference to @Before method. However, the @Before from the UI tests step definitions still gets executed for the API features. Here's an example of how I separated the files( before, I had them in the exact same package even though the image is showing in different packages): https://screencast.com/t/ht5Jz4cLC

    • I tried to create new packages for the types of tests such as .api and .ui. This works when I run through IntelliJ, but doesn't work when I execute "mvn test". Seems that no tests are found or executed. Here is how this setup looks: https://screencast.com/t/uSlB4sYTFm

    • I tried to set a static property in one of my test methods that would decide if I have an API test and then update implementation in setUp() based on that. This of course didn't work because setUp() gets executed before the actual test that knows if it's a UI or API test.

Is there a way to change the behavior of setUp in an automated way so that it executes/doesn't execute the appropriate logic based on the test type (API/UI)?


Answer:

You can use tagged hooks to do this: "Hooks can be conditionally selected for execution based on the tags of the scenario. To run a particular hook only for certain scenarios, you can associate a Hook with a tag expression." from the docs.

Question:

I'm using cucumber-jvm in my integration tests and I need to execute some code after all scenarios are finished, just once.

After reading carefully some posts like this and reviewed this reported issue, I've accomplished it doing something like this:

public class ContextSteps {

   private static boolean initialized = false;

   @cucumber.api.java.Before
   public void setUp() throws Exception {
      if (!initialized) {
         // Init context. Run just once before first scenario starts

         Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
              // End context. Run just once after all scenarios are finished
            }
         });

         initialized = true;
      }
   }
}

I think the context initialization (equivalent to BeforeAll) done in this way is fine. However, although it's working, I'm not sure at all if the AfterAll simulation using Runtime.getRuntime().addShutdownHook() is a good practice.

So, these are my questions:

  • Should I avoid Runtime.getRuntime().addShutdownHook() to implement AfterAll?
  • Are there other better choices to emulate the AfterAll behaviour in cucumber-jvm?

Thanks in advance for the help.


Answer:

Probably a better way would be to use a build tool, like Ant, Maven or Gradle for set-up and tear-down actions, which are part of integration tests.

When using Maven Fail Safe Plug-in, for setting up integration tests. There is the phase pre-integration-test, which is typically used for setting up the database and launch the web-container. Then the integration-tests are run (phase integration-test). And afterwards the phase post-integration-test is run, for shutting down and closing / removing / cleaning up things.

INFO In case the Cucumber tests are run through JUnit, the following might also be worth considering

In case it is simpler, smaller set up stuff, you can have a look at the JUnit @BeforeClass and @AfterClass. Or implement a JUnit @ClassRule, which has it's own before() and after() methods.

@ClassRule
public static ExternalResource resource = new ExternalResource() {
  @Override
  protected void before() throws Throwable {
    myServer.connect();
  }

  @Override
  protected void after() {
    myServer.disconnect();
  }
};

Question:

I need a way to execute some code before each cucumber scenarios start and know which scenario is about to start, to make the executed code specific to this scenario.

I have try to override the Cucumber runner to create a hook without success. Do you have any solution for me ?


Answer:

Anything you write inside @Before is executed before every Scenario. Then, you could get especific scenario info using getName() for instance.

@Before
public void before(Scenario scenario) {
    String scenarioName = scenario.getName();
}

Take a look to this example I've made and see working Hooks.

Question:

I'll try to explain how I've built this, I imagine something obvious may jump out.

I'm relatively new to this but am building a cucumber-appium framework and am running into some trouble. Currently, I initialise my Appium driver in my @Before hook which is in a GlobalHooks class which contains all Hooks. I have altered the hook so part of it only runs at the start of the test run as Cucumber doesn't support global hooks and I don't see why I should initialise the driver before every test (I'm using Junit so cannot take advantage of TestNG's @BeforeSuite feature).

To take advantage of Appium's parallel sessions, I now want to make my driver (declared in a GlobalHooks class and defined in the @Before method in that class) non-static and it's presenting issues across the suite.

Is it wise to define my driver in a Hook like this if I want my Page Classes to use this driver? Or does anyone have any advice on how to initialise non-static drivers so they can be used to run parallel Appium sessions?

This is probably more of a Java question than about Cucumber or Appium.


Answer:

This is a stripped down version of using selenium drivers in parallel. It should be similar to adopt to appium driver. This uses pico-container for object creation and sharing across a scenario. Need to add cucumber-picocontainer dependency.

DriverFactory stores all the drivers in a ThreadLocal variable drivers.

public final class DriverFactory {

    private static ThreadLocal<WebDriver> drivers = new ThreadLocal<>();
    //To quit the drivers and browsers at the end only. 
    private static List<WebDriver> storedDrivers = new ArrayList<>();

    static {
        Runtime.getRuntime().addShutdownHook(new Thread(){
            public void run(){
                storedDrivers.stream().forEach(WebDriver::quit);
            }
          });
    }

    private DriverFactory() {}

    public static WebDriver getDriver() {
        return drivers.get();
    }

    public static void addDriver(WebDriver driver) {
        storedDrivers.add(driver);
        drivers.set(driver);
    }

    public static void removeDriver() {
        storedDrivers.remove(drivers.get());
        drivers.remove();
    }   
}

Exists solely for allowing pico-container to create the required driver. Checks if driver already exists for thread to reuse. To avoid this case you can look at extending ThreadLocal class and setting up the initialValue() method.

public class SharedDriver {
    public SharedDriver() {
        if (DriverFactory.getDriver() == null) {
        System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");
        DriverFactory.addDriver(new ChromeDriver());
    }
    }   
}

public class GoogleHomePO extends LoadableComponent<GoogleHomePO>{

    @FindBy(name="q")
    private WebElement searchTextBox;

    public GoogleHomePO() {
        DriverFactory.getDriver().get("https://www.google.com/");
        PageFactory.initElements(DriverFactory.getDriver(), this);
    }

    public void enterSearch(String search) {
        searchTextBox.sendKeys(search);
    }
}

The SharedDriver class needs to be added to any one step definition constructor in the project. As cucumber initializes all the step and hook classes for each scenario picocontainer will instantiate the driver object if required and store it in the DriverFactory.

public class StepDefinition {

    private GoogleHomePO gmPO;

    public StepDefinition(SharedDriver driver, GoogleHomePO gmPO) {
        this.gmPO = gmPO;
    }

    @Given("Go to google page")
    public void given() {
        gmPO.get();
    }

    @When("Enter search {string}")
    public void when(String search) {
        gmPO.enterSearch(search);
    }    
}

Feature file 1

Feature: 

  Scenario: First
    Given Go to google page
    When Enter search "From Feature One"

  Scenario: First Again
    Given Go to google page
    When Enter search "From Feature One Again Again"

Feature file 2

Feature:

  Scenario: Second
    Given Go to google page
    When Enter search "From Feature Two"

POM settings

<plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M3</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                        <configuration>
                            <includes>
                                <include>**/*Runner.java</include>
                            </includes>

                            <parallel>methods</parallel>
                            <useUnlimitedThreads>true</useUnlimitedThreads>

                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>

Question:

@Before and @After methods of hooks are not running while running the Runner class.

I am using the dependencies: cucumber-java 4.3.0 cucumber-jvm 4.3.0

All steps in stepdef file are running fine except hooks. Is it some issue with latest cucumber version?

public class Hooks {
@Before
public void beforeHooks() {
    System.out.println("Run Before Scenario");
}

@After
public void afterHooks() {
    System.out.println("Run After Scenario");
}

Answer:

First Make sure you are using cucumber.api.java.Before (interface) rather than org.junit.Before as Cucumber will not process JUnit annotations.

  • @Before - import cucumber.api.java.Before;
  • @After - import cucumber.api.java.After;

Hope we are on the same page here and let's move further without making any delay.

Second lets understand in case your STEPS IMPLEMENTATION METHODS and HOOK CLASS is in the same package then we do not need to specify path of Hooks class additionally in the glue option of runner. In my case, i do have both in same package so we need to set only one package.

But if they are in the different packages then please include the package of the Hooks class in the glue option of the runner file.

Cucumber Runner :

package com.jacksparrow.automation.suite.runner;

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

@RunWith(Cucumber.class)
@CucumberOptions(features = "classpath:features/functional/",
                     glue = {"com.jacksparrow.automation.steps_definitions.functional" },
                   plugin = { "pretty","json:target/cucumber-json/cucumber.json",
                            "junit:target/cucumber-reports/Cucumber.xml", "html:target/cucumber-reports"},
                   tags = { "@BAMS_Submitted_State_Guest_User" },
                   strict = false,
                   dryRun = false,
               monochrome = true)

public class RunCukeTest {
}

Key Point: We shall not mix direct & transitive dependencies specially their versions! Doing so can cause unpredictable outcome. You can add below set of cucumber minimal dependencies.

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-junit</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-picocontainer</artifactId>
    <version>4.3.0</version>
    <scope>test</scope>
</dependency>

Question:

While I was reading how to add a cleanup for a cucumber scenario on failure I got this piece of code in the internet.

After do |s|
  if s.failed?
    #If you are on an iOS Device
        $driver.quit
         sleep(time_for_driver_ready)
    #else
         reset  
  end
end

This is in ruby. I am working in java and is there any way to implement s.failed? in java, because in java the after method declaration does not include a scenario variable.


Answer:

You can pass the Scenario as a parameter to the After hook, the framework will inject the current scenario object to the method.

@After
public void afterMethod(Scenario scenario) {
    if(scenario.isFailed()) {


    }
}

Question:

Cucumber 'After' Hook not working?

I have a DriverFactory class which performs the setup etc as listed below however once all steps have been executed the 'Cucumber After' method dosnt seem to work which is housed in the DriverFactory?

I want a master hooks class 'Before' 'After' etc etc which will stop code duplication within Step files

public class DriverFactory  {
protected WebDriver driver;
protected BasePage basePage;
protected LoginPage loginPage;

public WebDriver getDriver() {
    if(driver == null) {
        System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "\\src\\test\\java\\resources\\other\\chromedriver.exe");
        this.driver = new ChromeDriver();
        this.driver.manage().window().maximize();
        this.driver.manage().timeouts().pageLoadTimeout(20, TimeUnit.SECONDS);
    }
    return this.driver;
}

public WebDriver returnDriver() {
    return this.driver;
}


@After
public void test() throws Throwable {
    this.driver.close();
    this.driver.quit();
}

}

public class LoginSteps {
DriverFactory driverFactory = new DriverFactory();
WebDriver driver = driverFactory.getDriver();

@Given("^User navigates to the \"([^\"]*)\" website$")
public void user_navigates_to_the_website(String url) throws Throwable {
    BasePage basePage = new BasePage(driver);
    basePage.loadUrl(url);
}

@And("^User entered the \"([^\"]*)\" username$")
public void user_entered_the_username(String username) throws Throwable {
    LoginPage loginPage = new LoginPage(driver);
    loginPage.setUsername(username);
}

Answer:

I guess, the global hooks are not supported by Cucumber-jvm. However, there are some workarounds available. Please refer to link1 and link2 for details about the workarounds.

Question:

I`m trying to implement cucumber with Page Object Model and i faced couple problems and have lots of questions.

  1. My iOS app is not that complex, but i still want to orginize all stepdefs and features to correspond with pages from POM. So i will have multiple stepdefs and runners. What is best practice to organize all of it ? I tried Pico DI, but wasn`t even able to pass my driver instance through it.( If you can, please provide structure solution)

  2. Since its native iOS - Im not going to close app after every scenario( it will take forever). But I still want to keep features DRY for re-usability What would be the best way to create one appium driver instance and never create another until feature is executed? I understand i just need to add driver.quit in the last step. What i`m straggling with is to use same driver throughout all project(Pages, Stepdefs) (see code)

  3. Im going to run tests with TestNg and wonder if @Before @After annotations still work in stepdefs or it`s better to avoid them ?

  4. The MAIN question : Dependency Injection with pico. Since all my tests are acceptance (end to end feature tests) i wonder if it is good idea to create one InjectionSetUp class which will contain all my pages AND driver

Driver manager

public class IOSDriverManager {
    public static ThreadLocal<IOSDriver<IOSElement>> webDriver = new ThreadLocal<IOSDriver<IOSElement>>();

 public static DesiredCapabilities getIOSCapsLocal() {
        DesiredCapabilities caps = new DesiredCapabilities();
         //My caps
  return caps;

 public static void createThreadLocalWebDriver() {
        IOSDriver<IOSElement> driver = null;
//try catch
      driver = new IOSDriver<IOSElement>(new URL(APPIUM_SERVER_URL), getIOSCapsLocal());
//try catch
        webDriver.set(driver);
 }
 public static IOSDriver<IOSElement> getThreadLocalDriver() {
        IOSDriver<IOSElement> driver = webDriver.get();
        if (driver == null) {
            createThreadLocalWebDriver();
            driver = webDriver.get();
        }
        return driver;
    }

BasePage

public class BasePage {
    IOSDriver<IOSElement> Driver;
    public BasePage(IOSDriver<IOSElement> driver) {
     initElements();
     Driver = driver;
    }

    private void initElements() {

        PageFactory.initElements(new AppiumFieldDecorator(getDriver()),this);
    }

    protected IOSDriver<IOSElement> getDriver() {
        return IOSDriverManager.getThreadLocalDriver();
    }

}

AnyPage

public class BiosurveyPage extends BasePage {
 public BiosurveyPage(IOSDriver<IOSElement> driver) {
        super(driver); //  Appiumfield  decorator is applied by BasePage constructor
//Is it better to just use getDriver() method throughout all pages instead of declaring driver again ? 
    }

Finaly StepDefs

public class newSignUpFlowTest{
    protected IOSDriver<IOSElement> getDriver() {
        return IOSDriverManager.getThreadLocalDriver();
    }
    LoginPage poLogin = new LoginPage(getDriver());
    SignUpPage poSignup = new SignUpPage(getDriver());
      // 10+ pages 

@Given("I am on Login Page")
    public void iAmOnLoginPage() {
        assertThat("ON LOGIN PAGE",poLogin.isLoginScreen(),equalTo(true));
    }

    @When("I tap on Sign Up Link")
    public void iTapsOnSignUpLink() {
        poLogin.clickSignUp();
    }
// 20+ methods for this feature

UPDATE I fixed everything and able to run tests. Now my question is - does my framework look decent ? I dont have any real life experience prior to this. So can someone just approve me and suggest enhancements to keep up with best industry practices ? I know this post might piss some people off, but i dont know where else to communicate this as i don`t have any friends in the QA field and working remotely


Answer:

When using dependency injection you want to let your DI system do all the heavy lifting. So your step definitions have a constructor dependency on your page objects.

public class NewSignUpFlowStepDefinitions {

    private final LoginPage poLogin;
    private final SignUpPage poSignup;

    NewSignUpFlowStepDefinitions(LoginPage poLogin, SignUpPage poSignup) {
        this.poLogin = poLogin;
        this.poSignup = poSignup;
    }

    @Given("I am on Login Page")
    public void iAmOnLoginPage() {
        assertThat("ON LOGIN PAGE", poLogin.isLoginScreen(), equalTo(true));
    }

    @When("I tap on Sign Up Link")
    public void iTapsOnSignUpLink() {
        poLogin.clickSignUp();
    }

}

You page objects can't have a constructor dependency on IOSDriver<IOSElement> because PicoContainer can only create dependency chains that don't end with empty constructors. So instead we use the IOSDriverManager here.

public class BiosurveyPage extends BasePage {
    public BiosurveyPage(IOSDriverManager driverManager) {
        super(driverManager);
    }
}

In your BasePage you then unpack the webdriver from the driver manager.

public abstract class BasePage {
    private IOSDriverManager driverManager;

    public BasePage(IOSDriverManager driverManager) {
        this.driverManager = driverManager;
        initElements();
    }

    private void initElements() {
        PageFactory.initElements(new AppiumFieldDecorator(driverManager.getDriver()), this);
    }

    protected IOSDriver<IOSElement> getDriver() {
        return driverManager.getDriver();

    }
}

Then in the IOSDriverManager you can keep a reference to the webdriver and create it as needed. If you are certain it is safe to share your webdriver between scenarios you can still use a ThreadLocal here.

public class IOSDriverManager implements Disposable{
    private IOSDriver<IOSElement> webDriver;

    private DesiredCapabilities getIOSCapsLocal() {
        DesiredCapabilities caps = new DesiredCapabilities();
        return caps;
    }

    private void createWebDriver() {
        webDriver = new IOSDriver<IOSElement>(new URL(APPIUM_SERVER_URL), getIOSCapsLocal());
    }

    public IOSDriver<IOSElement> getDriver() {
        if (webDriver == null) {
            createThreadLocalWebDriver();
        }
        return webDriver;
    }

    @Override
    public void dispose() {
        // Stop webdriver ehre
    }
}

Note that Disposable adds the dispose method which will let you clean up your driver a after each scenario. Either to dispose it entirely or to reset it to some known state.

http://picocontainer.com/lifecycle.html

Question:

I trying to update my knowledge in Cucumber, but some features are very hard to find or change. Right know I'm trying and failing to find the way. I tried and can use in step definitions various hooks @After and define the order or conditional hooks like a featured tag but can't do both. Any idea if it's possible to do both in the newest version?! If yes, how to do it? Please!

https://cucumber.io/docs/cucumber/api/?sbsearch=CucumberOptions#hooks

exemple (for the tag use):

@After("not @unit")
public void screenshot(Scenario scenario) {
    if (scenario.isFailed()) {
        byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
        scenario.embed(screenshot, "image/png");
    }
}
@After("not @unit")
    public void closeBrowser() {
        driver.quit();
}

exemple (for the order):

@After(order = 10)
public void screenshot(Scenario scenario) {
    if (scenario.isFailed()) {
        byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
        scenario.embed(screenshot, "image/png");
    }
}
@After(order = 0)
    public void closeBrowser() {
        driver.quit();
}

In old cukes version it was possible to use:

@After(order = 1, value={"~@unit"})

Answer:

Hooks no longer take multiple tags. Instead you use a single tag expression. So the correct way to use hooks would be:

@After(order = 1, value="(@firefox or @chrome) and not @unit")
public void screenshot(Scenario scenario) {

}

Question:

I am very new to Automated testing and Cucumber too. I have written a simple cucumber-Junit example with three scenarios.

I am expecting @Before to be called in the beginning of each scenario and @After to be called after each scenario. So in total they should be executed only three times as there are three scenarios.

When I run my following step definition, first of all @Before is called, then @After, then again @Before, then Scenario 1, then @After, then @Before, then Scenario 2, then @After, then directly Scenario 3. Hence No @Before and @After is called before and after scenario 3 respectively. On the other hand they are called twice before the execution of first scenario. Any idea what I am doing wrong here? I don't have any .rb file in my project for Hook.

My @Before contains: System.out.println("Setup performed"); and my @After contains System.out.println("CleanUp Performed");

import cucumber.api.PendingException;
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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

public class CalculatorSteps {
    private Calculator calculator;

    @Before
    public void setUp() {
        calculator = new Calculator();
        System.out.println("Setup Performed...");
    }

    //Scenario : add two numbers - With regular expression
    @Given("^I have a calculator$")
    public void i_have_a_calculator() {
        assertNotNull(calculator);
    }

    @When("^I add (\\d+) and (\\d+)$")
    public void i_add(int arg1, int arg2) {
        calculator.add(arg1, arg2);
    }

    @Then("^the result should be (\\d+)$")
    public void the_result_should_be(int result) {
        assertEquals(result, calculator.getResult());
    }

    //Scenario : Subtract one number from another - With regular expression

    @Given("^I have a calculatorr$")
    public void i_have_a_calculator1() throws Throwable {
        assertNotNull(calculator);
    }

    @When("^I subtract (\\d+.\\d+) from (\\d+.\\d+)$")
    public void i_subtract_from(int arg1, int arg2) {
    calculator.subtract(arg1, arg2);
    }

    @Then("^the result should be (\\d+.\\d+)$")
    public void the_result_should_be1(double result1) {
        assertEquals(result1, calculator.getresult1(), 0.5);
    } 

    @After
    public void cleanUp() {
        System.out.println("CleanUp Performed...");
    }
}

Feature File looks like :

    Feature: Calculator
      I use Calculator instead of calculating myself

      #Scenario: Add two numbers
        #Given I have a calculator
        #When I add 2 and 3
        #Then the result should be 5

     @smokeTest
      Scenario Outline: Add two numbers
        Given I have a calculator
            When I add <num1> and <num2>
        Then the result should be <ans>

        Examples:
        | num1 | num2 | ans |
        | 2    | 3    | 5   |
        | 4    | 5    | 9   |

      @regressionTest
      Scenario: Subtract one number from another
        Given I have a calculator
        When I subtract 2.5 from 7.5
        Then the result should be 5.0

Output looks like:

    Feature: Calculator
      I use Calculator instead of calculating myself
    Setup Performed...
    CleanUp Performed...
    Setup Performed...

      Scenario Outline: Add two numbers # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:19
        Given I have a calculator       # CalculatorSteps.i_have_a_calculator()
        When I add 2 and 3              # CalculatorSteps.i_add(int,int)
        Then the result should be 5     # CalculatorSteps.the_result_should_be(int)
    CleanUp Performed...
    Setup Performed...

      Scenario Outline: Add two numbers # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:20
        Given I have a calculator       # CalculatorSteps.i_have_a_calculator()
        When I add 4 and 5              # CalculatorSteps.i_add(int,int)
        Then the result should be 9     # CalculatorSteps.the_result_should_be(int)
    CleanUp Performed...

      #@regressionTest
      Scenario: Subtract one number from another # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:23
        Given I have a calculator                # CalculatorSteps.i_have_a_calculator()
        When I subtract 2.5 from 7.5             # CalculatorSteps.i_subtract_from(int,int)
        Then the result should be 5.0            # CalculatorSteps.the_result_should_be1(double)

    3 Scenarios (3 passed)
    9 Steps (9 passed)
    0m0,074s

After having tried counter and @DaveyDaveDave code, the output looks like below: The printing of counter lines in the beginning are not at correct place, but atleast sems to be executed correctly. But I am still wondering why would it not execute @Before, @Given and @After in the case of the last Subtract scenario.

Feature: Calculator
  I use Calculator instead of calculating myself
@Before Count is: 0
@Given Count is: 1
@After Count is: 2
@Before Count is: 3

  @smokeTest
  Scenario Outline: Add two numbers # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:19
    Given I have a calculator       # CalculatorSteps.i_have_a_calculator()
    When I add 2 and 3              # CalculatorSteps.i_add(int,int)
    Then the result should be 5     # CalculatorSteps.the_result_should_be(int)
@Given Count is: 4
@After Count is: 5
@Before Count is: 6

  @smokeTest
  Scenario Outline: Add two numbers # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:20
    Given I have a calculator       # CalculatorSteps.i_have_a_calculator()
    When I add 4 and 5              # CalculatorSteps.i_add(int,int)
    Then the result should be 9     # CalculatorSteps.the_result_should_be(int)
@After Count is: 7

  @regressionTest
  Scenario: Subtract one number from another # src/test/java/cucumber/junit/maven/cucumber_jvm/maven/calculatorFeature.feature:23
    #Given I have a calculator
    When I subtract 2.5 from 7.5             # CalculatorSteps.i_subtract_from(int,int)
    Then the result should be 5.0            # CalculatorSteps.the_result_should_be1(double)

3 Scenarios (3 passed)
8 Steps (8 passed)
0m0,072s

Answer:

I think the feature is executing in the order you expect, and that this is just an issue of how and when the output appears.

I've just copied and pasted your source, and added a counter variable, and when I run it I see the following output:

Setup Performed... 0
In given...1
...CleanUp Performed... 2
Setup Performed... 3
In given...4
...CleanUp Performed... 5
Setup Performed... 6
In given...7
...CleanUp Performed... 8

In other words, the numbers show that the ordering of these things is happening as you expect.

If it helps to convince you, I've created the following RequiresSetupCalculator which throws exceptions if it hasn't been set up before performing an operation, and also if we try to run the setup without having previously called cleanUp.

public class RequiresSetupCalculator {
  private static RequiresSetupCalculator instance;

  private int result = 0;
  private double result1 = 0;

  private boolean isSetup;

  private RequiresSetupCalculator() {
  }

  public static synchronized RequiresSetupCalculator setup() {
    if (instance == null) {
        instance = new RequiresSetupCalculator();
    }

    if (instance.isSetup) {
        throw new RuntimeException("I have already been set up");
    }

    instance.isSetup = true;

    return instance;
  }

  public void cleanUp() {
    if (!isSetup) {
        throw new RuntimeException("I wasn't set up in the first place");
    }

    isSetup = false;
  }

  public void add(final int arg1,
                final int arg2) {
    if (!isSetup) {
        throw new RuntimeException("I haven't been set up, no calculating for you!");
    }

    result = arg1 + arg2;
  }

  public int getResult() {
    if (!isSetup) {
        throw new RuntimeException("I haven't been set up, no calculating for you!");
    }

    return result;
  }

  public void subtract(final int arg1,
                     final int arg2) {
    if (!isSetup) {
        throw new RuntimeException("I haven't been set up, no calculating for you!");
    }

    result1 = arg2 - arg1;
  }

  public double getresult1() {
    if (!isSetup) {
        throw new RuntimeException("I haven't been set up, no calculating for you!");
    }

    return result1;
  }
}

To test this, if you copy the above into a new class, alongside your existing Calculator, then modify your CalculatorSteps to use the RequiresSetupCalculator and to call the setup and cleanUp methods in the @Before and @After methods of your tests like this:

public class CalculatorSteps {
  private RequiresSetupCalculator calculator;

  @Before
  public void setUp() {
    calculator = RequiresSetupCalculator.setup();
    System.out.println("Setup Performed...");
  }

  ... (existing test steps unchanged)...

  @After
  public void cleanUp() {
    System.out.println("CleanUp Performed...");
    calculator.cleanUp();
  }
}

Now, when you run your test, if it still passes, I think we can be sure that the @Before method runs before every test, once and only once, and that the @After method runs after every test, also once and only once.

For me, this is working successfully. If you get exceptions being thrown when you run the tests, then there is something strange going on, but I'm fairly confident you will get the same results as me.

Question:

I'm not clear why I am getting 3 chrome browsers opening for the following example. I have an @Before (cucumber version) annotation to simply setup a chrome webdriver instance before the scenario runs. As far as I can see, it should open one browser, run scenario (step defs) then close using the @After cucumber hook. What happens is 2 windows open before a third and final window actually executes the steps:

Scenario:
    Given I am on the holidays homepage
    When I select the departure location "LON"
    And I select the destination location "PAR"
    And I submit a search
    Then search results are displayed

Step Def:

public class FindAHolidayStepDefs {

    private WebDriver driver;

    @Before
    public void setup() {
        System.setProperty("webdriver.chrome.driver",
                System.getProperty("user.dir") + "\\drivers\\chromedriver.exe");
        driver = new ChromeDriver();
    }

    @Given("^I am on the Holidays homepage$")
    public void IAmOnTheThomasCookHomepage() {
        driver.get("http://uat7.co-operativetravel.co.uk/");
        driver.manage().window().maximize();
        String pageTitle = driver.getTitle();

        assertEquals("the wrong page title was displayed !", "Holidays - welcome", pageTitle);
    }


    @When("^I select the departure location \"([^\"]*)\"$")
    public void ISelectTheDepartureLocation(String departureAirport) {
        WebElement dropDownContainer = driver.findElement(By.xpath("(//div[@class=\"custom-select departurePoint airportSelect\"])[1]"));
        dropDownContainer.click();

        selectOption(departureAirport);
    }

    @When("^I select the destination location \"([^\"]*)\"$")
    public void ISelectTheDestinationLocation(String destinationAirport) {
        WebElement destinationField = driver.findElement(By.xpath(("(//div[@class=\"searchFormCol destinationAirport\"]/div[@class=\"combinedInput searchFormInput\"]/span/input)[1]")));
        destinationField.sendKeys(destinationAirport);
        selectOption("(" + destinationAirport + ")");
    }


    @When("^I submit a search$")public void iSubmitASearch() throws Throwable {
        WebElement submitButton = driver.findElement(By.xpath("(.//*[@type='submit'])[1]"));
        submitButton.click();
    }


    @Then("^search results are displayed$")
    public void searchResultsAreDisplayed() throws Throwable {
        waitForIsDisplayed(By.xpath(".//*[@id='container']/div/div[3]/div/div[1]/div/h3"), 30);
        assertThat(checkPageTitle(), equalTo("Package Results"));
    }


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

When I step through the code in Intellij, the following method is called:

private void runHooks(List<HookDefinition> hooks, Reporter reporter, Set<Tag> tags, boolean isBefore) 

and Intellij reports that hooks paramter = 3 at this point.

hooks:  size=3   reporter: "null"  tags:  size = 0  isBefore: true

Answer:

  • Do you have more then one scenario in your feature? -> The @Before method will be executed before each scenario.
  • Do you have a different stepdef class with @Before annotated method that opens chrome? -> All @Before methods will be called before a scenario is executed.

Question:

So I have a pretty basic project using Appium/Cucumber/Gherkin in Java with JUnit. I originally wanted to use @Before and @After tags in each of my StepDefinition classes to create the driver and quit the driver and submit a report. The problem i'm running into is that even if none of my steps exist inside the step definition class, the class itself is still created which causes my @Before and @After tags to run.

I have a workaround for the initialization by basically moving my driver creation to a background step. But I haven't been able to figure out a way to properly use my driver quit and report functionality. I'm pretty sure I could do the same for my after case if I forced the functionality into the last step in each of my scenarios, but I was hoping for a cleaner more global approach.

Is this working as intended? or should the Step Definition classes only be instantiated when one of their functions is actually used? Have you run into this before?


Answer:

Hooks in cucumber are global in nature. That is if the hook methods are coded in any class defined in the package structure given to the glue option of CucumberOption they will be executed.

The way out is to use tags as filters in the Before and After hooks. And also you can fix up the order of execution of these tags.

If you want a scenario to open browser in Before hook, give the scenario a @Browser tag. And in the @Before annotation use it like @Before("@Browser"). This will only run for scenarios with the Browser tag. Same goes for After hook.

You can add the order attribute to Before and After tags to give precedence. @Before(value="@Browser", order=5). This will run before a Before hook method with order of say 10. For After hook order of 10 will run before one with 5.

Question:

We always need to store cucumber scenario hooks on same package where the glue code stored but I want to store it in main framework code so is it possible in cucumber to keep aside scenario hooks from glue code package for better project structure.


Answer:

You can place the hook codes anywhere you want, as long as you include the package containing the hook classes in the glue option of the runner class. Add all the relevant packages by delimiting with a comma.

Question:

Iam using cucumber CLI and need use the hooks @Before, @After, @BeforeClass, @AfterClass. I declare the hooks in a class of my project, but cucumber ignores them when I run with CLI

public class Runner {

    public static void main(String[] args) throws Exception {   

        args2 = new String[] { path + "/features", "--glue", "stepFiles",
                "--threads", threadsQty, "", "--tags", tags};

        cucumber.api.cli.Main.run(args2,Thread.currentThread().getContextClassLoader());


    }

    @Before
    public void beforeScenario() {
        System.out.println("This will run before the Scenario");
    }

    @After
    public void afterScenario() {
        System.out.println("This will run after the Scenario");
    }
}

Answer:

the hook class must be added as glue. try

args2 = new String[] { path + "/features", "--glue", "stepFiles", "--glue","<your hook class>",  "--threads", threadsQty, "", "--tags", tags};

Question:

I'm getting the error: cucumber.runtime.CucumberException: Failed to instantiate class steps.MyStepdefs

Here's what I'm trying to do. My hooks are located in the package steps:

public class hooks {
    public static WebDriver webDriver;

    @Before
    public static void ChromeDriverSetup() {

        System.out.println("Creating new ChromeDriver instance...");
        webDriver = new ChromeDriver();

        System.out.println("Hello from hooks!");
    }

The above is executed...

But the MyStepdefs test class does not execute (it is also in the steps package) & I get the above error.

public class MyStepdefs {
   ProductPage productPageObjects = new ProductPage();


    @Given("I purchase {int} items of the same product")
    public void iPurchaseItemsOfTheSameProduct(int qty)  {

        System.out.println("Hello from MySteps!");
        productPageObjects.Visit();
        productPageObjects.ClickPlusQtyElement(qty);
    }
package pageobjects;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import static steps.hooks.webDriver;

public class ProductPage {

    private WebElement totalQtyElement = webDriver.findElement(By.cssSelector(".sanitized"));
    private WebElement plusQtyElement = webDriver.findElement(By.cssSelector(".sanitized"));

    public void Visit() {
        webDriver.get("https://www.example.com");
    }


    public String ClickPlusQtyElement(int qty) {

        int minAmount = 1;
        while (minAmount < qty)
        {
            plusQtyElement.click();
            minAmount ++;

        }
        System.out.println("The amount is now: " + totalQtyElement.getText());

        return totalQtyElement.getText();
    }
}

In IntelliJ, my glue is set as steps. I also have a RunCucumberTest class in the steps package.

package steps;

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

@RunWith(Cucumber.class)
@CucumberOptions(tags = "not @ignore", plugin = {"pretty", "html:target/cucumber"})
public class RunCucumberTest {}

Why does it not execute MyStepsdefs ?

stacktrace: https://pastebin.com/X5KhHfuP

Update: When I comment out calls to ProductPage the line System.out.println("Hello from MySteps!"); does get executed. So there is an issue with that particular call.


Answer:

Ok I figured it out. When I try to instantiate the class ProductPage, I get an error due to the web driver calls i.e. private WebElement totalQtyElement = webDriver.findElement(By.cssSelector(".sanitized"));

The problem is that I haven't visited a URL yet! So, I am going to put the above in a method and do some refactoring.

Question:

I want to access all the cucumber scenario steps in @before hook. Is there a way to do this?

I have tried passing the cucumber scenario object in the before hook method but it only provides the basic info like scenario.getName(), scenario.getId(). What I require is something like getSteps() which give me the List<String> of all the steps of that particular scenario.

What I am looking for is something like this

    @Before("@dev")
public void testcase(Scenario scenario){

    for (Step a : scenario.getSteps()) {
        System.out.println("scenario step = " + a.getText());
    }
}

Basically I want the complete scenario information at the beginning of the test execution itself.

If I pass the argument of class cucumber.api.TestCase in the before method then I can access the getTestSteps() method but that leads to below exception.

cucumber.runtime.CucumberException: When a hook declares an argument it must be of type cucumber.api.Scenario. public void com.thermofisher.bid.spa.kingfisher.ui.steps.Hooks.poc(cucumber.api.TestCase)

Answer:

Try something like this:

@Before
public void setUp(Scenario scenario) throws Exception{

    tags = (ArrayList<String>) scenario.getSourceTagNames();
    System.out.println("At Hooks: " + scenario.getId());
    Iterator ite = tags.iterator();

    while (ite.hasNext()) {

        String buffer = ite.next().toString();
        if (buffer.startsWith("<tagOfATestCase>")) {

            Field f = scenario.getClass().getDeclaredField("testCase");
            f.setAccessible(true);
            TestCase r = (TestCase) f.get(scenario);

            List<PickleStepTestStep> testSteps = r.getTestSteps().stream().filter(x -> x instanceof PickleStepTestStep)
                    .map(x -> (PickleStepTestStep) x).collect(Collectors.toList());

            for (PickleStepTestStep ts : testSteps) {

                System.out.println(ts.getStepText());//will print your test case steps

            }

        }

    }

Question:

I am currently using cucumber(info.cukes)-Selenium to run automation test.

Now, I have a situation where a specific step can occur at any point of the flow. So, I have to design a cucumber scenario to verify the dynamic page in every step.

How I can implement this without AfterStep hook? (cucumber(info.cukes) won't support AfterStep hook)

Example:

Scenario: Complect the order.

Given: Open URL with chrome browser
When:  Login with correct ID and password
Then: Complect the details on step 1
And: Complect the details on step 2
And: Complect the details on step 3

My application has a dynamic page which can appear between any pages, so I need to check if the page is displayed or not in every step and the execute the specifc task when the dynamic page is displayed and then move to the next step in the scenario.

Could you please someone help me to achieve this scenario with cucumber Selenium automation.

Thanks for your help.


Answer:

When it comes to keeping end-to-end test code DRY, page objects are almost always the answer (or at least, they're a great place to start). Even if you had the AfterStep hook, I'd caution about adding too much implicit stuff there, it can be a real headache to follow the flow and debug, especially for others.

In your case, I could imagine a page object for the three pages in the workflow, and each one has a clickSubmit() method that checks for the URL of the mystery page and completes it if present. Something like

public void clickSubmit() {
  click(By.className("submitButton"));

  if (driver.getCurrentUrl().contains("mysterypage")) {
      MysteryPage mysteryPage = new MysteryPage(driver);
      mysteryPage.completeForm();
      mysteryPage.clickSumbit();
  }
}

Admittedly, its a little strange for a method called clickSubmit to be doing all that, so maybe it would be better for a helper method to exist up in the test, and just be called at the end of each step.

As an afterthought, if you have real business rules around when and where this intermediate page shows up, and it's not just random, it may be worth capturing in the gherkin. If the user really cares that it shows up here and not there, but you made the gherkin blind to its appearance so it always "just works", you could be masking off a bug.

Question:


Answer:

When using TestNG & Cucumber together, we shall not use @Before (cucumber.api.java.Before) to read values from testng.xml rather we shall use @BeforeTest (org.testng.annotations.BeforeTest) Below is an example of Hooks.java

import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import cucumber.api.testng.AbstractTestNGCucumberTests;

public class Hooks extends AbstractTestNGCucumberTests {

    @Parameters({ "browser" })
    @BeforeTest
    public void setUpScenario(String browser){
        //BaseSteps.getInstance().getBrowserInstantiation(browser);
    }
}