为了账号安全,请及时绑定邮箱和手机立即绑定

当我有很多 SpringBootTest 类时,如何有效地使用嵌套配置类来注入依赖项

当我有很多 SpringBootTest 类时,如何有效地使用嵌套配置类来注入依赖项

BIG阳 2024-01-05 15:02:55
这个问题是Can I use code to control the dependency resolution Decisions made by ApplicationContext in Spring Boot? 的续集。@SpringBootTest可接受的答案是在每个测试夹具类中定义一个嵌套类,对其进行注释@TestConfiguration并在其中为需要解析的每个 bean 定义一个工厂方法。嵌套类的影响范围仅限于测试装置,影响装置中的所有测试,但不影响其他装置中定义的测试。当在每个测试装置中运行测试时,这提供了对注入组件的依赖项的细粒度控制。这种方法的问题是它需要在每个测试夹具类中添加一个嵌套的解析器类。这是不可扩展的。考虑一个具有 10 个测试装置的项目。其中 9 个使用相同的注入依赖项,只有第 10 个仅需要一个特定接口的不同实现。在这种情况下,我需要将测试配置类复制到 9 个测试夹具类中,并仅在第 10 个测试中使用第二个配置类。我需要一种更具可扩展性的方法来做到这一点。例如,在上面的情况下,我希望能够定义两个配置类,一个用于测试装置使用的两种配置中的每一种。然后我希望能够为每个测试装置指定应使用两个配置类中的哪一个。我努力了:我尝试使用注释将一个文本夹具的嵌套配置类导入到另一个测试夹具中@Import,但这样做时,后者中的配置类被忽略。我还尝试将嵌套配置类移动到上层,以便它可以用于未明确将不同配置类定义为嵌套类的所有测试装置,但在这种情况下,所有测试装置都会忽略配置类。总之,我正在寻找一种有效的方法,该方法允许我只编写每个配置类一次,然后有选择地将一个配置类应用于每个 SpringBootTest 类,而无需复制它。
查看完整描述

1 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

经过一些实验,我得到了以下解决方案。

背景

  1. 我们有两个接口:IClient 和 IServer

  2. IClient 有两种实现:RealClient 和 MockClient。

  3. IServer 有两种实现:RealServer 和 MockServer。

要求

  1. 生产代码(在 main/java 中)应该使用两者的 Real 实现。

  2. 测试夹具(在test/java中用@SpringBootTest注释)

  • InterfaceTests 定义了应该使用 MockServer 和 MockClient 的测试

  • ClientTests 定义了应该使用 MockServer 和 RealClient 来测试 RealClient 的测试。

  • ServerTests 定义了应该使用 MockClient 和 RealServer 来测试 RealServer 的测试。

  • IntegrationTests 定义应使用 RealServer 和 RealClient 的测试

从上面可以清楚地看出,模拟/真实客户端/服务器有四种组合,并且每种组合在代码的某些区域都需要。

解决方案

该解决方案利用 @Configuration 和 @TestConfiguration 注释来实现这些要求,而无需重复代码。

  1. 不要使用 @Component 注释接口及其实现

  2. 在main/java下实现一个配置类如下:


@Configuration

public class RealInjector {

    @Bean

    public IServer createServer(){

        return new RealServer();

    }


    @Bean

    public IClient createClient(){

        return new RealClient();

    }

}


在test/java下实现这三个测试配置类

@TestConfiguration

public class AllMockInjector {

    @Bean

    public IServer createServer(){

        return new MockServer();

    }


    @Bean

    public IClient createClient(){

        return new MockClient();

    }

}


@TestConfiguration

public class MockServerInjector{

    @Bean

    public IServer createServer(){

        return new MockServer();

    }


    @Bean

    public IClient createClient(){

        return new RealClient();

    }

}


@TestConfiguration

public class MockClientInjector{

    @Bean

    public IServer createServer(){

        return new RealServer();

    }


    @Bean

    public IClient createClient(){

        return new MockClient();

    }

}


将 InterfaceTests 测试装置注释如下:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {AllMockInjector.class})

public class InterfaceTests { ... }

将 ClientTests 测试装置注释如下:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {MockServerInjector.class})

public class ClientTests { ... }

将 ServerTests 测试装置注释如下:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {MockClientInjector.class})

public class ServerTests { ... }

按如下方式注释 IntegrationTests 测试装置:

@RunWith(SpringRunner.class)

@SpringBootTest(classes = {RealInjector.class})

public class IntegrationTests { ... }

最后

为了让测试配置类覆盖 main/java 中的 RealInjector 配置类,我们需要设置属性:


spring.main.allow-bean-definition-overriding=true 

一种方法是对上述每个测试装置进行注释,如下所示:


@SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])

class TestFixture { ... }

但这非常冗长,特别是如果您有很多测试装置。相反,您可以在 test/resources 下的 application.properties 文件中添加以下内容:


spring.main.allow-bean-definition-overriding=true

您可能还需要将其添加到 main/resources 下的 application.properties 中。


概括

该解决方案使您能够对注入到生产和测试代码中的实现进行细粒度控制。该解决方案不需要重复代码或外部配置文件(除了 test/resources/application.properties 中的一个属性)。


查看完整回答
反对 回复 2024-01-05
  • 1 回答
  • 0 关注
  • 95 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信