springcucumber-jvmcucumber-javacucumber-junit

Conditional Cucumber Steps class


I might be a little bit light on the mechanism of how Cucumber works with Spring, but my current understanding is that if there's a class on the classpath with some Cucumber hook in it, the Cucumber Spring engine will add that class/bean to the Spring context. E.g.

class MySteps(val someDep: SomeDep) {
  @Before
  fun initialize() {
  }
}

Is there any way how to disable the instantiation of this class based on certain conditions, e.g. presence of SomeDep class on the classpath? I'm thinking of an analogy to @ConditionalOnClass. Ideally, I would expect that this annotation would work out of the box but apparently it isn't.

The use case is that MySteps is in a common company-wide Cucumber library (which I can modify) and it makes sense precisely for the apps that have SomeDep on the classpath.


Solution

  • Cucumber discovers steps on the "glue path". This usually defaults the entirely class path. The glue path is a list of package names in which Cucumber should look for step definitions, hooks, ect.

    If you're using Cucumber with JUnit 5, you'd configure it like through the cucumber.glue property:

    import org.junit.platform.suite.api.ConfigurationParameter;
    import org.junit.platform.suite.api.IncludeEngines;
    import org.junit.platform.suite.api.SelectPackages;
    import org.junit.platform.suite.api.Suite;
    
    import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
    import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
    
    @Suite
    @IncludeEngines("cucumber")
    @SelectPackages("io.cucumber.skeleton")
    @ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
    @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "io.cucumber.skeleton")
    public class RunCucumberTest {
    }
    

    There are no other mechanisms by which you can control which step definitions are available.

    Note that people have tried to make common libraries of steps in the past. Most notably Aslak himself and it didn't make for good Gherkin.

    Instead consider sharing only the API. If sufficiently rich, the step definitions should be an extremely thin wrapper. There will be some duplication, but that duplication also provides flexibility.

    This flexibility allows people writing and implementing the tests to use expressive context sensitive language. From a maintenance perspective it is also easier to evolve an API in Java than a set of company wide step definitions in Gherkin. And on top of that you can use the API for other tests as well.

    For your API you may still want to do something in Spring prior to each Scenario though. For this you can use Springs [Test execution listeners][2]. cucumber-spring integrates with Spring TestContextManager so that should mostly work as expected.

    Should you want to activate some manually, you can use the @CucumberContextConfiguration annotation.

    @CucumberContextConfiguration
    @TestExecutionListeners(...your test execution listeners)
    @SpringBootTest
    public class CucumberSpringConfiguration {
    
    }