I am writing a new app and trying to do BDD using cucumber and Spring Boot 1.4. Working code is as shown below:
@SpringBootApplication
public class Application {
@Bean
MyService myService() {
return new MyService();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
public class MyService {}
Test code is as shown below:
@RunWith(Cucumber.class)
public class RunFeatures {}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
public class MyStepDef {
@Autowired
MyService myService;
@Given("^Some initial condition$")
public void appIsStarted() throws Throwable {
if (service == null) throw new Exception("Dependency not injected!");
System.out.println("App started");
}
@Then("^Nothing happens$")
public void thereShouldBeNoException() throws Throwable {
System.out.println("Test passed");
}
}
Feature file is as shown below:
Feature: Test Cucumber with spring
Scenario: First Scenario
Given Some initial condition
Then Nothing happens
When I run the above as is, all works well and dependency (MyService) is injected into MyStepDef with no issues.
If I replace this code:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)
With the code below (New way to handle it in Spring Boot 1.4):
@RunWith(SpringRunner.class)
@SpringBootTest
Then the dependency (MyService) never gets injected. Am I missing something perhaps?
Thanks in advance for your help!!!
@RunWith(SpringRunner. class) tells JUnit to run using Spring's testing support. SpringRunner is the new name for SpringJUnit4ClassRunner , it's just a bit easier on the eye. @SpringBootTest is saying “bootstrap with Spring Boot's support” (e.g. load application.
Cucumber scans your classes with step definitions in them, passes them to PicoContainer, then asks it to create new instances for every scenario.
What is Cucumber Options? In layman language, @CucumberOptions are like property files or settings for your test. Basically @CucumberOptions enables us to do all the things that we could have done if we have used cucumber command line.
I had the same problem. The comment from above directed me to the solution
The problematic code in cucumber-spring seems to be this github.com/cucumber/cucumber-jvm/blob/master/spring/src/main/…
After adding the annotation @ContextConfiguration
the tests are working as expected.
So what i've got is the following...
@RunWith(Cucumber.class)
@CucumberOptions(plugin = {"json:target/cucumber.json", "pretty"}, features = "src/test/features")
public class CucumberTest {
}
@ContextConfiguration
@SpringBootTest
public abstract class StepDefs {
}
public class MyStepDefs extends StepDefs {
@Inject
Service service;
@Inject
Repository repository;
[...]
}
I hope this helps you further
I got it working with Spring Boot 1.5.x and 2.0 and then wrote a blog post to try to clarify this since it's tricky.
First, even if it's obvious, you need to have the right dependencies included in your project (being cucumber-spring
the important one here). For example, with Maven:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
<version>2.3.1</version>
<scope>test</scope>
</dependency>
Now, the important part to make it work, summarized:
@RunWith(Cucumber.class
. @Given
, @When
, @Then
, etc.). @SpringBootTest
, @RunWith(SpringRunner.class)
and any other configuration you need to run your test with Spring Boot. For instance, if you're implementing an integration test without mocking other layers, you should add the webEnvironment
configuration and set it to RANDOM_PORT
or DEFINED_PORT
. See the diagram and the code skeleton below.
The entry point:
@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/features/bag.feature", plugin = {"pretty", "html:target/cucumber"})
public class BagCucumberIntegrationTest {
}
The Spring Boot base test class:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class SpringBootBaseIntegrationTest {
}
The step definitions class:
@Ignore
public class BagCucumberStepDefinitions extends SpringBootBaseIntegrationTest {
// @Given, @When, @Then annotated methods
}
This is what you need to make DI work. For the full code example, just check my blog post or the code in GitHub.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With