简体   繁体   English

在单体 SpringBoot 应用程序中创建集成测试

[英]Creating integration tests in a monolithic SpringBoot application

I was asked to create an integration test for a service within a very large SpringBoot project yielding dozens of implemented services.我被要求为一个非常大的 SpringBoot 项目中的服务创建一个集成测试,该项目产生了数十个已实现的服务。 When the application is executed all of these services are deployed - I want to avoid deploying all services unrelated with the one for which I'm creating the test.当应用程序执行时,所有这些服务都会被部署——我想避免部署与我为其创建测试的服务无关的所有服务。 Unfortunately, I do not (yet) have as much experience with spring boot tests as I would hope for hence I was wondering what is the best approach to address this.不幸的是,我(还)没有我希望的那样丰富的 spring 启动测试经验,因此我想知道解决这个问题的最佳方法是什么。

I was thinking about annotating all unrelated services with @MockBean annotation and all related ones with @Autowire inside the test class, but I'm not sure if this is the proper way to go. Can anyone point me in the right direction?我正在考虑在测试 class 中使用@MockBean注释注释所有不相关的服务,并使用@Autowire注释所有相关服务,但我不确定这是否是 go 的正确方法。任何人都可以指出我正确的方向吗?

Thank you.谢谢。

The answer largely depends on the scope of your integration test.答案很大程度上取决于集成测试的范围。 I will try to cover two main ways and you can google your wait our for more examples and details.我将尝试涵盖两种主要方式,您可以在 google 上搜索更多示例和详细信息。 Spring Boot testing documentation is also your friend. Spring Boot 测试文档也是您的朋友。

Slices切片

Spring Boot provides test utilities called slices . Spring Boot 提供了称为slices 的测试实用程序。 For example there's a slice for testing your controllers - @WebMvcTest - this test will load all configuration for calling your application from HTTP and your specified controller ( @WebMvcTest(YourController.class) ).例如,有一个用于测试您的控制器的切片 - @WebMvcTest - 此测试将加载用于从 HTTP 和您指定的控制器( @WebMvcTest(YourController.class) )调用您的应用程序的所有配置。 After that you need to decide what to do with dependencies of that controller.之后,您需要决定如何处理该控制器的依赖项。

You can:你可以:

  • Mock them with @MockBean .@MockBean模拟它们。
  • Provide real implementation (or additional configuration) with @Import (and then you have to again deal with dependencies of the newly imported dependency).使用@Import提供真正的实现(或附加配置)(然后您必须再次处理新导入的依赖项的依赖项)。
  • Load additional part of Spring Boot auto-configuration.加载 Spring Boot 自动配置的附加部分。 This can be done using @AutoConfigureSomething annotations.这可以使用@AutoConfigureSomething注释来完成。 - All slices are basically composites of autoconfigure annotations and you are free to add them to your tests. - 所有切片基本上都是自动配置注释的组合,您可以自由地将它们添加到您的测试中。 For example have a look at annotations on DataJpaTest for what it takes to add capability to setup Spring Boot Data JPA with test database.例如,查看DataJpaTest上的注释,了解添加使用测试数据库设置 Spring Boot Data JPA 所需的功能。

You can have maximum one slice per your test but you can import any number of additional services, configurations, mocks, auto-configurations etc. The point is - you choose what is configured for your test;每个测试最多可以有一个切片,但您可以导入任意数量的附加服务、配置、模拟、自动配置等。重点是 - 您选择为测试配置的内容; new unrelated services with new dependencies should not break existing tests.具有新依赖项的新无关服务不应破坏现有测试。

SpringBootTest启动测试

Another approach is @SpringBootTest annotation - this goes in the opposite direction - by default it loads everything and you can exclude stuff you don't want with @MockBean , @EnableAutoConfiguration(exclude=SomeClass) etc.另一种方法是@SpringBootTest注释 - 这在相反的方向 - 默认情况下它加载所有内容,您可以使用@MockBean@EnableAutoConfiguration(exclude=SomeClass)等排除您不想要的@MockBean

There's of course a danger of breaking existing tests when adding new services.在添加新服务时,当然存在破坏现有测试的危险。 - This should not happen too often as everything is configured automatically but it's still a possibility especially in monolith with more configuration. - 这不应该经常发生,因为一切都是自动配置的,但它仍然是一种可能性,尤其是在具有更多配置的单体中。

If you have to create spring integrations tests you have to : - invoke spring Context by using annotation on test class - for instance : @RunWith(SpringJUnit4ClassRunner.class) - use @MockBean or @SpyBean annotation on services that you are not going to test but they are part of testing methods/class - use @Autowired annotation on class that you are going to test.如果您必须创建 spring 集成测试,您必须: - 通过在测试类上使用注释来调用 spring 上下文 - 例如:@RunWith(SpringJUnit4ClassRunner.class) - 在您不打算测试的服务上使用 @MockBean 或 @SpyBean 注释但它们是测试方法/类的一部分 - 在您要测试的类上使用 @Autowired 注释。 To verify results you can use Junit4 or Junit5 asserts and for verifying behavior you can use Mockito要验证结果,您可以使用 Junit4 或 Junit5 断言,验证行为您可以使用 Mockito

I had a similar issue of wanting to be able to isolate the integration testing of a particular component (or service) in Springboot;我有一个类似的问题,希望能够在 Springboot 中隔离特定组件(或服务)的集成测试; my constraint is that I have to use JUnit5 and Mockito.我的约束是我必须使用 JUnit5 和 Mockito。

I found these 2 posts from Baeldung which as usual only gives a specific answer to a specific scenario and not a general solution: https://www.baeldung.com/injecting-mocks-in-spring https://www.baeldung.com/junit-5-runwith我从 Baeldung 找到了这两个帖子,它们像往常一样只给出了特定场景的特定答案,而不是一般解决方案: https://www.baeldung.com/injecting-mocks-in-spring https://www.baeldung。 com/junit-5-runwith

Long story short I found a solution as follows:长话短说,我找到了如下解决方案:

  1. Create a config class as follows for your component(s) in a test folder for your integration test (the example below is my wanting to integration test an Interceptor component; I have redacted names):在集成测试的测试文件夹中为您的组件创建如下配置 class(下面的示例是我想要集成测试拦截器组件;我已经编辑了名称): 在此处输入图像描述

  2. You can mock out beans, or you can instantiate beans that have no dependencies (leaf beans so to say)您可以模拟 bean,也可以实例化没有依赖项的 bean(可以说是叶 bean)

  3. Then in your integration test you can use the latter config.然后在您的集成测试中,您可以使用后一个配置。 in order to then get your component under test (cut) have its dependencies satisfied.为了让您的组件在测试(剪切)下满足其依赖性。 My Interceptor had 3 levels of dependencies:我的拦截器有 3 个依赖级别: 在此处输入图像描述

  4. Finally, in your integration tests you can use the following to mock out method calls of the beans you mocked out: when(cut.getInitRequestRepo().initRequest(ArgumentMatchers.any(InParamInitRequest.class))).thenReturn(outParamInitRequest);最后,在您的集成测试中,您可以使用以下方法来模拟您模拟出的when(cut.getInitRequestRepo().initRequest(ArgumentMatchers.any(InParamInitRequest.class))).thenReturn(outParamInitRequest);的方法调用:

The POM dependencies I used:我使用的 POM 依赖项:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
            <scope>test</scope>
        </dependency>
 <!-- Testing with Mocks -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>3.11.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-junit-jupiter</artifactId>
            <version>3.11.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.7.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.10.0</version>
            <scope>test</scope>
        </dependency>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM