3

I have inherited from others a big project of testing whose main Java classes are CommonSteps, CommonBase and CommonScript. They are currently related in this way:

CommonSteps extends CommonBase

CommonBase extends CommonScript

The problem is with the next method when I try to run the project with mvn clean install:

@After
public void tearDown(Scenario scenario) {
  if (scenario.isFailed()) {
    // Take a screenshot...
    final byte[] screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
    scenario.embed(screenshot, "image/png"); // ... and embed it in the report.
  }
}

I get the error:

cucumber.runtime.CucumberException: You're not allowed to extend classes that define step definitions or hooks: steps.CommonSteps extends class common.CommonBase.

How could I start working with dependency injection deleting everything related to inheritance?

Grisha Levit
  • 8,194
  • 2
  • 38
  • 53
Jesús
  • 31
  • 1
  • 1
  • 3
  • Is there a restriction of having step or hook classes without extending the base classes. The code you have added is pretty standard. It should work if you cut-paste this method to another class or a new class which does not extend another step or hook class. – Grasshopper Feb 01 '17 at 18:04
  • For reusing same step definitions someone created a Class CommonSteps and included it in the glue path. So its definitions are available for all feature files in the test. However, moving this method to another class which does not extend another step of hook class does not work. – Jesús Feb 02 '17 at 08:37
  • In which class is the method located? – tom Feb 11 '17 at 11:30
  • Possible duplicate of [Cucumber class extending step definitions and hooks](http://stackoverflow.com/questions/34771928/cucumber-class-extending-step-definitions-and-hooks) – Roberto Pegoraro Apr 26 '17 at 19:23

4 Answers4

4

Cucumber creates a new instance of all classes defining stepdefs before each scenario. It then invokes stepdef methods on one of those instances whenever it needs to run a step.

If you defined a stepdef method foo in class A and you have a class B extends A you'd get an a and b instance. The foo method would be available on both instances, and Cucumber would not be able to decide what instance to invoke the method on.

That's why we don't allow it.

The solution is to use composition instead of inheritance. You can achieve composition with dependency injection - Cucumber supports several popular DI frameworks.

0

As has been stated, Cucumber does not allow you to extend step definition base classes. I have just hit this error attempting to re-use a suite of step defs across two different integration test profiles (app is SpringBoot+Java8 but crucially, we write our Cucumber Step definitions in Groovy..).

The solution / workaround I found was to define the common, re-usable, annotated step definitions in a Groovy trait, then have one or more concrete classes implement that trait and simply define / override any behaviour from the trait and most importantly, declare the Spring int-test profile I want to run with. This seems to work well.

Tom Bunting
  • 1,845
  • 16
  • 24
0

you can define your @Before or @After steps in stepdefinition and then add method in base class

stepdefinition:

   @After
public void after(Scenario scenario) {
    this.scenario = scenario;
}

baseclass method:

    public static Scenario scenario;

    public Scenario tearDown(Scenario scenario){

        if (scenario.isFailed()) {
    final byte[] screenshot = ((TakesScreenshot)driver).getScreenshotAs(OutputType.BYTES);
    scenario.embed(screenshot, "image/png"); 
   }
 return scenario;
 }
Nitish Kumar
  • 161
  • 1
  • 4
  • 16
0

I had the same problem and solved it following the approach described here.

Use PicoContainer to achieve dependency injection and share state.

Here you can find the latest version considering the date of this post: https://mvnrepository.com/artifact/io.cucumber/cucumber-picocontainer/7.2.3

You just need to add the PicoContainer's dependency and that's it. No configuration is needed.

I've used constructor dependency injection. I don't know if PicoContainer supports other kinds of dependency injection, like a setter method or annotations.

Considering the classes that you have mentioned, when applying dependency injection they should be like the below example:

public class CommonScript {
}


public class CommonBase {
    private final CommonScript commonScript;

    public CommonBase(CommonScript commonScript) {
        this.commonScript = commonScript;
    }
}


public class CommonSteps {
    private final CommonBase commonBase;

    public CommonSteps(CommonBase commonBase) {
        this.commonBase = commonBase;
    }
}

Following the above structure, you can:

  1. create step definitions and reuse them in your ".feature" files;
  2. define hooks;
  3. share state;

Reference:

Lucas
  • 124
  • 3