以编程方式运行黄瓜测试时将服务注入到步骤定义中

问题描述 投票:0回答:1

鉴于代码在此处找到我能够以编程方式运行黄瓜测试。

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上下文被正确创建,我们可以在步骤定义类中注入其他服务。

以编程方式运行测试时如何获得相同的行为(注入服务、共享状态)? (按照我的例子)

cucumber bdd cucumber-java cucumber-junit cucumber-spring
1个回答
0
投票

因此,要使其正常工作,请使用以下包结构:

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
© www.soinside.com 2019 - 2024. All rights reserved.