spring-boot - Spring Boot单元测试中的@SpringBootTest vs @ContextConfiguration vs @Import

标签 spring-boot

我正在做一个Spring Boot项目。
我正在基于“ TDD”编写“单元测试”代码,这有点困难。

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

因此,我使用了@SpringBootTest的类名称。

我已正常完成测试,但不确定使用@ContextConfiguration与使用@Import之间的区别。

这三个选项均正常运行。我想知道哪种选择是最好的。

@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个选项都是不好的。
单元测试必须非常快,您应该能够在一秒钟左右的时间里运行数百个(当然,这取决于硬件,但是您可以理解)。
因此,一旦您说“我为每项测试都开始做弹簧”,它就不再是单元测试了。
每个测试的启动弹簧是非常昂贵的操作。

有趣的是,您的CoffeeService代码是以可完美测试的方式编写的:只需使用Mockito之类的库来模拟存储库类,然后就可以测试服务逻辑而完全没有任何弹性。
您将不需要任何弹簧滑块,任何弹簧注释。您还将看到这些测试的运行速度更快。

  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");
        }
  }


现在关于弹簧测试库

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

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

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

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

您提供的所有方法都会过滤应该运行的内容,要加载和注入的bean。

通常,这些限制适用于“层”,而不适用于单个bean(层=其余层,数据层等)。

第二和第三种方法实际上是相同的,它们是“过滤”应用程序上下文的方式,仅保留必要的bean。

更新:

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

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


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


不,它不能,它是一个集成测试,在春季仅运行一个类。

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

如果您使用spring运行一个类,那么在任何情况下,您都必须模拟其所有依赖关系,可以通过模仿来完成...

现在关于您的问题


@ContextConfiguration与@SpringBootTest的技术差异。


@SpringBootTest仅在您具有Spring Boot应用程序时才相关。该框架在幕后使用Spring,但是总的来说,它附带了许多如何编写应用程序“基础结构”的预定义配方/实践:
- 配置管理,
-包装结构,
-可插拔性
-记录
-数据库集成等

因此,Spring Boot建立了定义明确的流程来处理上述所有项目,如果您想启动模仿Spring Boot应用程序的测试,则可以使用@SpringBootTest批注。否则(或者如果您只有弹簧驱动的应用程序而没有弹簧靴)-完全不要使用它。

@ContextConfiguration是完全不同的东西。它只是说明您要在Spring驱动的应用程序中使用什么bean(它也可以在spring boot中使用)


“单元测试”是使用@ContextConfiguration的正确方法吗?或不?


就像我说的那样-所有与弹簧测试有关的东西仅用于集成测试,所以不,这是在单元测试中使用的错误方法。对于单元测试,应该使用完全不使用spring的东西(例如模拟的mockito和没有springRunner的常规junit测试)。

关于spring-boot - Spring Boot单元测试中的@SpringBootTest vs @ContextConfiguration vs @Import,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56712707/

相关文章:

java - 我们如何在同一个 Spring 应用程序中同时添加 JWT 和基本身份验证?

java - spring webflux CORS header 被删除

java - 从存储库解析插件 'spring:boot' 的版本时出错

java - 在 Spring mvc 中使用 html 文件发送 dto

java - 使用 Spring + Thymeleaf 时出现 java.lang.StackOverflowError

spring-mvc - Spring Boot + Spring MVC + Ratpack 可能吗?

java - if...else Spring Boot 模拟测试中的语句问题

java - 使用 Jackson 反序列化时禁用标量到字符串的转换

spring-boot - Mockito:监视功能接口(interface)内的函数调用?

java - 我在 Spring Boot 中遇到了 Bean 问题