【问题标题】:Cucumber with Spring Boot 1.4: Dependencies not injected when using @SpringBootTest and @RunWith(SpringRunner.class)Cucumber with Spring Boot 1.4:使用 @SpringBootTest 和 @RunWith(SpringRunner.class) 时未注入依赖项
【发布时间】:2026-01-12 16:40:01
【问题描述】:

我正在编写一个新应用程序并尝试使用 cucumber 和 Spring Boot 1.4 进行 BDD。工作代码如下:

@SpringBootApplication
public class Application {
    @Bean
    MyService myService() {
        return new MyService();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

public class MyService {}

测试代码如下:

@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: Test Cucumber with spring
    Scenario: First Scenario
        Given Some initial condition
        Then Nothing happens

当我按原样运行上述程序时,一切正常,并且依赖项(MyService)被注入到 MyStepDef 中,没有任何问题。

如果我替换此代码:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Application.class, loader = SpringApplicationContextLoader.class)

使用下面的代码(在 Spring Boot 1.4 中处理它的新方法):

@RunWith(SpringRunner.class)
@SpringBootTest

然后依赖项(MyService)永远不会被注入。也许我错过了什么?

提前感谢您的帮助!!!

【问题讨论】:

标签: java spring-boot cucumber-jvm


【解决方案1】:

我让它与 Spring Boot 1.5.x 和 2.0 一起使用,然后 wrote a blog post 试图澄清这一点,因为它很棘手。

首先,即使很明显,您也需要在项目中包含正确的依赖项(cucumber-spring 是这里重要的依赖项)。例如,使用 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>

现在,让它发挥作用的重要部分,总结一下:

  • 测试的入口点应该是一个带有@RunWith(Cucumber.class 注释的类。
  • 该类将使用步骤定义,这些定义通常在一个带有注释方法的单独类中(@Given@When@Then 等)。
  • 诀窍是这个类应该扩展一个带有@SpringBootTest@RunWith(SpringRunner.class) 注释的基类以及使用 Spring Boot 运行测试所需的任何其他配置。例如,如果您在不模拟其他层的情况下实现集成测试,则应添加webEnvironment 配置并将其设置为RANDOM_PORTDEFINED_PORT

请参阅下面的图表和代码骨架。

入口点:

@RunWith(Cucumber.class)
@CucumberOptions(features = "src/test/resources/features/bag.feature", plugin = {"pretty", "html:target/cucumber"})
public class BagCucumberIntegrationTest {
}

Spring Boot 基础测试类:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class SpringBootBaseIntegrationTest {
}

步骤定义类:

@Ignore
public class BagCucumberStepDefinitions extends SpringBootBaseIntegrationTest {
  // @Given, @When, @Then annotated methods
}

这是使 DI 发挥作用所需要的。对于完整的代码示例,只需检查 my blog postcode in GitHub

【讨论】:

    【解决方案2】:

    我已经让它在 Spring Boot 1.5 中工作。我想和你分享配置:

    pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            ...
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.7.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
            ...
        <dependencies>
                ...
            <dependency>
                <groupId>info.cukes</groupId>
                <artifactId>cucumber-java</artifactId>
                <version>1.2.5</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>info.cukes</groupId>
                <artifactId>cucumber-spring</artifactId>
                <version>1.2.5</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>info.cukes</groupId>
                <artifactId>cucumber-junit</artifactId>
                <version>1.2.5</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
            ...
    </project>
    

    功能文件

    Feature: User should be greeted
    
      Background:
        Given The database is empty
        Then All connections are set
    
      Scenario: Default user is greeted
        Given A default user
        When The application is started
        Then The user should be greeted with "Hello Marc!"
    

    黄瓜钩

    @RunWith(Cucumber.class)
    @CucumberOptions(features = "src/test/resources", strict = true)
    public class CucumberTests {    // Classname should end on *Tests
    
    }
    

    抽象 Spring 配置

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @ContextConfiguration
    abstract class AbstractSpringConfigurationTest {
    
    }
    

    胶水

    class CucumberGlue : AbstractSpringConfigurationTest() {
    
        @Autowired
        lateinit var restTemplate: TestRestTemplate
    
        @Autowired
        lateinit var restController: RestController
    
        @Autowired
        lateinit var personRepository: PersonRepository
    
        @Autowired
        lateinit var entityManager: EntityManager
    
        private var result: String? = null
    
        @Given("^The database is empty$")
        fun the_database_is_empty() {
            personRepository.deleteAll()
        }
    
        @Then("^All connections are set$")
        fun all_connections_are_set() {
            assertThat(restTemplate).isNotNull()
            assertThat(entityManager).isNotNull()
        }
    
        @Given("^A default user$")
        fun a_default_user() {
        }
    
        @When("^The application is started$")
        fun the_application_is_started() {
            result = restController.testGet()
        }
    
        @Then("^The user should be greeted with \"([^\"]*)\"$")
        fun the_user_should_be_greeted_with(expectedName: String) {
            assertThat(result).isEqualTo(expectedName)
        }
    
    }
    

    【讨论】:

    • 这个答案可能会受益于通过添加一些解释、重用部分 OP 代码和名称以及摆脱解决问题不需要的多余代码来关注您如何解决特定问题
    【解决方案3】:

    这是我的配置,你可以试试

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
    @ContextConfiguration(classes = {Application.class})
    

    【讨论】:

      【解决方案4】:

      在 Spring Boot 1.4 之前你可以使用

      @ContextConfiguration(classes = {YourSpringConfiguration.class}, loader = SpringApplicationContextLoader.class)
      

      从 Spring Boot 1.4 开始,不推荐使用 SpringApplicationContextLoader,因此您应该改用 SpringBootContextLoader.class

      真的只是添加@SpringBootTest(带有可选的配置类)应该可以自己工作,但是如果您查看 cucumber.runtime.java.spring.SpringFactory 方法 annotatedWithSupportedSpringRootTestAnnotations 中的代码,它不会检查该注释,这就是为什么只需将该注释与 @SpringBootTest 一起添加即可。

      cucumber-spring 中的代码确实需要更改。我会看看是否可以提出问题,因为 Spring 文档中指出 SpringApplicationContextLoader 仅应在绝对必要时使用。我将尝试为此提出问题以支持黄瓜弹簧。

      因此,结合使用 @SpringBootTest 和 @ContextConfiguration 的 stripwire 是最好的解决方法。

      【讨论】:

        【解决方案5】:

        我遇到了同样的问题。上面的评论将我引向了解决方案

        cucumber-spring 中有问题的代码似乎是这个 github.com/cucumber/cucumber-jvm/blob/master/spring/src/main‌​/...

        添加注释@ContextConfiguration 后,测试按预期工作。

        所以我得到的是以下......

        @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;
        
            [...]
        
        }
        

        希望对你有所帮助

        【讨论】:

        • 这应该是 Spring Boot 1.4 的正确答案。甚至黄瓜官方文档也已过时-因为它使用了已弃用的注释,例如@IntegrationTest
        最近更新 更多