@SpringBootTest vs @ContextConfiguration vs @Import 在 Spring Boot 单元测试中 [英] @SpringBootTest vs @ContextConfiguration vs @Import in Spring Boot Unit Test

查看:23
本文介绍了@SpringBootTest vs @ContextConfiguration vs @Import 在 Spring Boot 单元测试中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一个 Spring Boot 项目.我正在写一个单元测试"基于TDD"的代码,这有点困难.

I'm working on a Spring Boot project. I'm writing a "Unit Test" code based on "TDD," which is a little bit difficult.

@SpringBootTest 加载了所有 BEAN,导致测试时间更长.

@SpringBootTest loaded all BEANs, which led to longer test times.

所以我使用了 @SpringBootTest 的类名称.

So I used the @SpringBootTest's class designation.

我正常完成了测试,但我不确定使用@ContextConfiguration 和使用@Import 之间的区别.

I completed the test normally, but I am not sure the difference between using @ContextConfiguration and using @Import.

所有三个选项都正常运行.我想知道哪种选择最好.

All three options run normally. I want to know which choice is the best.

@Service
public class CoffeeService {

    private final CoffeeRepository coffeeRepository;

    public CoffeeService(CoffeeRepository coffeeRepository) {
        this.coffeeRepository = coffeeRepository;
    }

    public String getCoffee(String name){
        return coffeeRepository.findByName(name);
    }
}

public interface CoffeeRepository {
    String findByName(String name);
}

@Repository
public class SimpleCoffeeRepository implements CoffeeRepository {

    @Override
    public String findByName(String name) {
        return "mocha";
    }
}

Option 1(SpringBootTest Annotation) - OK  
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {CoffeeService.class, SimpleCoffeeRepository.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }
}


Option 2 (ContextConfiguration Annoation) - OK
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }
}

Option 3 (Import Annoation) - OK
@RunWith(SpringRunner.class)
@Import({SimpleCoffeeRepository.class, CoffeeService.class})
public class CoffeeServiceTest {

    @Autowired
    private CoffeeService coffeeService;

    @Test
    public void getCoffeeTest() {
        String value = coffeeService.getCoffee("mocha");
        assertEquals("mocha", value);
    }

推荐答案

我认为 如果您的意图是运行适当的单元测试,那么所有 3 个提供的选项都是不好的.单元测试必须非常快,您应该能够在一秒钟左右的时间内运行数百个单元测试(当然取决于硬件,但您明白了).所以一旦你说我为每个测试开始春天"——它不再是一个单元测试.每次测试的启动弹簧都是一项非常昂贵的操作.

I think all 3 presented options are bad if your intent is to run a proper unit test. A unit test must be blazing fast, you should be able to run hundreds of those in a second or so (depending on the hardware of course, but you get the idea). So once you say "I start spring for each test" - it's not a unit test anymore. Starting spring for each test is a very expensive operation.

有趣的是,您的 CoffeeService 代码是以完全可测试的方式编写的:只需使用像 Mockito 这样的库来模拟存储库类,您就可以在没有任何 spring 的情况下测试服务逻辑所有.你不需要任何 spring runner,任何 spring 注释.您还会看到这些测试的运行速度要快得多.

What's interesting is that your code of CoffeeService is written in a way that it's perfectly testable: Just use some library like Mockito to mock the repository class and you can test the service logic without any spring at all. You won't need any spring runner, any spring annotations. You'll also see that these tests are running much faster.

  class MyServiceTest {

        @Test
        public void test_my_service_get_coffee_logic() {

               // setup
               CoffeeRepository repo = Mockito.mock(CoffeeRepository.class);
               Mockito.when(repo.findByName("mocha")).thenReturn("coffeeFound");

               CoffeeService underTest = new CoffeeService(repo);


               // when:
               String actualCoffee  =  underTest.getCoffee("mocha");

               // then:
               assertEquals(actualCoffee, "coffeeFound");
        }
  }

现在关于 spring 测试库

Now regarding spring test library

您可以将其视为一种测试代码的方法,该代码需要与其他组件进行一些互连,并且模拟所有内容都存在问题.它是同一个 JVM 中的一种集成测试.您提供的所有方式都运行应用程序上下文,这实际上是一个非常复杂的事情,在 youtube 上有关于应用程序上下文启动期间真正发生的事情的整个会话 - 尽管超出了问题的范围,重点是执行上下文启动需要时间

You can think about it as a way to test code that requires some interconnections with other components and its problematic to mock everything out. Its a kind of integration test inside the same JVM. All the ways that you've presented run an Application Context and this is a very complicated thing actually under the hood, there are entire sessions on youtube about what really happens during the application context startup - although, beyond the scope of the question, the point is that it takes time to execute the context startup

@SpringBootTest 更进一步,尝试模仿 Spring Boot 框架添加的创建上下文的过程:根据包结构决定要扫描的内容,从预定义的位置加载外部配置,可选择运行自动配置启动器和诸如此类.

@SpringBootTest goes further and tries to mimic the processes added by Spring Boot framework for creating the context: Decides what to scan based on package structures, loads external configurations from predefined locations optionally runs autoconfiguration starters and so on and so forth.

现在可能加载应用程序中所有 bean 的应用程序上下文可能非常大,并且对于某些测试,它不是必需的.它通常取决于测试的目的是什么

Now the application context that might load all the beans in the application can be very big, and for some tests, it's not required. Its usually depends on what is the purpose of the test

例如,如果您测试 rest 控制器(您已正确放置所有注释),您可能不需要启动数据库连接.

For Example, if you test rest controllers (that you've placed all the annotations correctly) probably you don't need to start up DB connections.

您提供的所有方式都过滤了应该运行的内容、加载的 bean 以及相互注入的 bean.

All the ways you've presented filter what exactly should be run, what beans to load and to inject into each other.

通常,这些限制应用于层"而不是单个 bean(层 = 休息层、数据层等).

Usually, these restrictions are applied to "layers" and not to single beans (layers = rest layer, data layer and so forth).

第二种和第三种方法实际上是相同的,它们是过滤"应用程序上下文的不同方式,只保留必要的 bean.

The second and third methods are actually the same, they are different ways to "filter" the application context preserving only the necessary beans.

更新:

由于您已经完成了方法的性能比较:

Since you've already done the performance comparison of the methods:

单元测试 = 非常快的测试,目的是验证您编写的代码(当然也可以是您的一位同事)因此,如果您运行 Spring,它会自动意味着一个相对较慢的测试.所以回答你的问题

Unit test = very fast test, it's purpose is to verify the code you've written (or one of your colleagues of course) So if you run Spring its automatically means a relatively slow test. So to answer your question

使用@ContextConfiguration 是否可以作为单元测试"

Whether using @ContextConfiguration can be a "Unit Test"

不,它不能,这是一个在 spring 中只运行一个类的集成测试.

No, it cannot, it's an integration test that runs only one class in spring.

通常,我们不会使用 Spring Framework 只运行一个类.如果只想测试一个类(一个单元)的代码,在 spring 容器中运行它有什么好处?是的,在某些情况下,它可以是几个类,但不是数十个或数百个.

Usually, we don't run only one class with Spring Framework. What is the benefit of running it inside the spring container if you only want to test the code of one class (a unit)? Yes, in some cases it can be a couple of classes, but not tens or hundreds.

如果您使用 spring 运行一个类,那么无论如何,您都必须模拟其所有依赖项,使用 mockito 也可以这样做...

If you run one class with spring then, in any case, you'll have to mock all its dependencies, the same can be done with mockito...

现在关于您的问题

@ContextConfiguration 与 @SpringBootTest 技术差异.

@ContextConfiguration vs. @SpringBootTest technical differences.

@SpringBootTest 仅在您拥有 Spring Boot 应用程序时才相关.该框架在底层使用 Spring,但简而言之,它附带了许多关于如何编写应用程序的基础设施"的预定义方法/实践:- 配置管理,- 封装结构,- 可插拔性- 记录- 数据库集成等

@SpringBootTest is relevant only if you have a Spring Boot application. This framework uses Spring under the hood but, in a nutshell, comes with many pre-defined recipes/practices of how to write the "infrastructure" of the application: - configuration management, - package structure, - pluggability - logging - database integration etc.

因此 Spring Boot 建立了明确定义的流程来处理上述所有项目,如果您想启动将模拟 Spring Boot 应用程序的测试,那么您可以使用 @SpringBootTest 注释.否则(或者如果您只有弹簧驱动的应用程序而不是弹簧靴) - 根本不要使用它.

So Spring Boot establishes well-defined processes to deal with all the aforementioned items, and if you want to start the test that will mimic the spring boot application, then you use @SpringBootTest annotation. Otherwise (or in case you have only spring driven application and not a spring boot) - don't use it at all.

@ContextConfiguration 是完全不同的东西.它只是说明你想在 Spring 驱动的应用程序中使用哪些 bean(它也适用于 spring boot)

@ContextConfiguration is an entirely different thing though. It just says what beans would you like to use in Spring driven application (it also works with spring boot)

单元测试"是使用@ContextConfiguration 的正确方法吗?还是不行?

Is "Unit Test" the correct way to use @ContextConfiguration? Or not?

正如我所说 - 所有与 spring 测试相关的东西仅用于集成测试,所以不,在单元测试中使用它是一种错误的方式.对于单元测试,请使用根本不使用 spring 的东西(例如用于模拟的 mockito 和没有 spring runner 的常规 junit 测试).

As I said - all the spring test related stuff is for integration testing only, so no, it's a wrong way to be used in unit tests. For unit tests go with something that doesn't use spring at all (like mockito for mocks and a regular junit test without spring runner).

这篇关于@SpringBootTest vs @ContextConfiguration vs @Import 在 Spring Boot 单元测试中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆