鉴于代码在此处找到我能够以编程方式运行黄瓜测试。
在step定义类中我已经注释了DummyService的构造函数注入。
如果我取消评论,那么我会收到(这是预期的)
o.cucumber.core.exception.CucumberException: class org.example.cucumberseviceinjection.StepDefinitions does not have a public zero-argument constructor.
To use dependency injection add an other ObjectFactory implementation such as:
* cucumber-picocontainer
* cucumber-spring
* cucumber-jakarta-cdi
为了解决这个问题,我在 pom.xml 下一个依赖项中取消注释:
<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-spring</artifactId>
</dependency>
如果我现在运行应用程序,我会收到以下异常:
io.cucumber.core.backend.CucumberBackendException: Please annotate a glue class with some context configuration.
For example:
@CucumberContextConfiguration
@SpringBootTest(classes = TestConfig.class)
public class CucumberSpringConfiguration { }
Or:
@CucumberContextConfiguration
@ContextConfiguration( ... )
public class CucumberSpringConfiguration { }
为什么当我在 runner 类中定义了glue属性
.configurationParameter("cucumber.glue", "org.example.cucumberseviceinjection")
时我会收到这个?
此外,我在步骤定义类上添加了缺少的注释。完成此步骤后,我收到:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.example.cucumberseviceinjection.StepDefinitions': Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'org.example.cucumberseviceinjection.DummyService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
基于文档我理解,当作为测试运行时,因为使用@SpringBootTest注释配置类,所以Spring上下文被正确创建,我们可以在步骤定义类中注入其他服务。
以编程方式运行测试时如何获得相同的行为(注入服务、共享状态)? (按照我的例子)
因此,要使其正常工作,请使用以下包结构:
src/main/java/
└── org
└── example
├── cucumberseviceinjection
│ ├── CucumberServiceInjectionApplication.java
│ └── CucumberTestRunner.java
└── glue
├── CucumberTestContext.java
├── DummyService.java
└── StepDefinitions.java
并且您必须使用单独的类来配置测试上下文。不是步骤定义类。一种方法是这样,但您也可以通过其他方式进行配置。最好阅读 Spring 文档。
@CucumberContextConfiguration
@ContextConfiguration
public class CucumberTestContext {
@Configuration
@ComponentScan(basePackages = "org.example.glue")
public static class TestConfiguration {
}
}
那么为什么这会起作用?
在这种情况下,有几个框架相互堆叠。
Spring -> JUnit Platform -> Cucumber JUnit Engine -> Cucumber Core -> Cucumber Spring -> Springs Test Context Manager -> Spring
春天在那里两次。一次作为运行 Cucumber 的应用程序,一次作为测试的依赖项注入容器。记住这一点很重要。
当您在 JUnit 平台上配置
cucumber.glue
时。然后将其传递给 Cucumber,Cucumber 使用它来查找类步骤定义(即用 @Given
、@When
、@Then
等注释的方法)。
对于每个场景,Cucumber 都会实例化所有步骤定义带注释的类。这确保了每个测试都是独立运行的——就像 JUnit 一样。尽管在使用 Cucumber Spring 时,应用程序上下文和所有非场景范围的 bean 都是共享测试。所以你一定要注意这一点。
为了实例化这些步骤定义及其依赖项,使用了 Cucumber Spring。特别是TestContextManager框架。并且测试上下文管理器需要知道如何构建应用程序上下文。
通常使用 JUnit 你会像这样使用它:
@SpringBootTest
public class ExampleTest {
...
}
并且因为
@SpringBootTest
是元注释的
@BootstrapWith(SpringBootTestContextBootstrapper.class)
@ExtendWith({SpringExtension.class})
JUnit 知道它应该使用
SpringExtension
,然后将 ExampleTest
传递给测试上下文管理器,测试上下文管理器会看到带有 BootstrapWith
的 SpringBootTestContextBootstrapper
注释,然后知道它应该寻找 @SpringBootApplication
注释的类来构建测试上下文。
但那是 JUnit,而不是 Cucumber。 Cucumber 没有测试类。它具有功能文件,但无法对其进行注释。 ;)
因此,我们必须用
@CucumberContextConfiguration
标记一个类,让 Cucumber Spring 知道将哪个类传递给测试上下文管理器,以便它可以构造测试应用程序上下文。 Cucumber Spring 在 cucumber.glue
中指定的包中查找该类。
那为什么不使用这个呢?
@CucumberContextConfiguration
@SpringBootTest
public class CucumberTestContext {
因为
@SpringBootTest
将指示测试上下文管理器寻找Spring应用程序,并且它将找到CucumberServiceInjectionApplication
。也可能是因为如果没有大量自定义,您无法在同一个 JVM 中同时运行两个 Spring Boot 应用程序。
为什么要分开包装
使用此功能时:
@CucumberContextConfiguration
@ContextConfiguration
public class CucumberTestContext {
@Configuration
@ComponentScan(basePackages = "org.example.glue")
public static class TestConfiguration {
}
}
Cucumber 知道将
CucumberTestContext
传递给测试上下文管理器。经理看到这是一个@ContextConfiguration
班。因此,将查找用 @Configuration
注释的内部类,并选择任何显式声明的配置(即 @ContextConfiguration(classes = ExampleConfiguration.class)
。
因为我们声明了一个内部类,所以
@ComponentScan
会启动,我们不希望它扫描托管应用程序的包。
那么如何完全以编程方式使用不同的应用程序上下文?
不幸的是这是不可能的。唯一的选择是声明多个应用程序上下文,将每个应用程序上下文放入单独的包中,并使用不同的配置集运行多个测试。
例如:
Test A:
cucumber.glue=org.example.glue.common,org.example.glue.config.a
Test B:
cucumber.glue=org.example.glue.common,org.example.glue.config.b