【问题标题】:Is it possible to test Spring REST Controllers without @DirtiesContext?是否可以在没有 @DirtiesContext 的情况下测试 Spring REST 控制器?
【发布时间】:2018-05-28 06:55:02
【问题描述】:

我正在开发一个 Spring-Boot Web 应用程序。编写集成测试的常用方法是:

@Test
@Transactional
@Rollback(true)
public void myTest() {
    // ...
}

只要只有 一个线程 工作,它就可以很好地工作。如果有多个线程,@Rollback 将无法工作。

但是,当使用 Spring REST 模板测试 @RestController 类时,总是多个线程(按设计):

  • 作为客户端运行 REST 模板的测试线程
  • 接收和处理请求的服务器线程

因此,您不能在 REST 测试中使用 @Rollback。问题是:您改用什么来使测试可重复并让它们在测试套件中很好地发挥作用?

@DirtiesContext 有效,但这是一个糟糕的选择,因为在每个 REST 测试方法之后重新启动 Spring 应用程序上下文会使套件的执行速度非常慢;每个测试需要几毫秒才能运行,但重新启动上下文需要几秒钟。

【问题讨论】:

    标签: java spring rest testing spring-boot


    【解决方案1】:

    首先,使用 Spring 上下文测试控制器不是单元测试。您应该考虑通过对依赖项使用模拟并创建standalone mock MVC 来为控制器编写单元测试:

    public class MyControllerTest {
      @InjectMocks
      private MyController tested;
      // add @Mock annotated members for all dependencies used by the controller here
      private MockMvc mvc;
    
      // add your tests here using mvc.perform()
      @Test
      public void getHealthReturnsStatusAsJson() throws Exception {
        mvc.perform(get("/health"))
          .andExpect(status().isOk())
          .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
          .andExpect(jsonPath("$.status", is("OK")));
      }
    
      @Before
      public void createControllerWithMocks() {
        MockitoAnnotations.initMocks(this);
        MockMvcBuilders.standaloneSetup(controller).build()
      }
    }
    

    如果您使用外部 @ControllerAdvice 进行错误处理等,这甚至可以通过在 MVC 构建器上调用 setControllerAdvice() 来实现。

    这样的测试并行运行没有问题,而且速度更快,根本不需要设置 Spring 上下文。

    您描述的部分集成测试对于确保使用正确的接线以及所有测试单元按预期协同工作也很有用。但我会更多地进行更通用的集成测试,包括多个/所有端点检查它们是否正常工作(不检查边缘情况)并模拟只接触外部的服务(如内部 REST 客户端,用内存中的一个替换数据库, ...)。通过此设置,您可以从一个新的数据库开始,甚至可能不需要回滚任何事务。这当然是使用像Liquibase 这样的数据库迁移框架最舒服的方法,它可以即时设置您的内存数据库。

    【讨论】:

    • 所以,基本上你所做的是直接调用 REST 控制器方法而不涉及应用程序容器(嵌入式或其他),对吗?这是一个有趣的方法,感谢您的指点。所以你单独测试你的“实体到json”映射?并且您对整个应用程序使用更大的、基于场景的集成测试?
    • @Alan47 不,在单元测试中使用模拟 mvc 时,您还将测试所有映射并且您不直接调用控制器方法。相反,您使用 mvc.perform 创建一个模拟请求,该请求映射到控制器,包括任何(默认)转换和(默认)消息转换器。如果您需要特殊的东西,您必须决定不在这里测试或进行更复杂的 MVC 构建(例如添加 HAL 支持或特殊转换服务设置)。
    • 听起来不错!我需要在 spring 文档中更多地研究这个 MockMVC。谢谢!
    • 我刚刚在我的应用程序中尝试了 MockMVC,它的工作原理非常棒,非常棒,比 Spring REST 模板好得多。感谢您向我指出这一点,由于“MVC”部分(我不使用 spring-webmvc),我总是忽略它,所以我认为这不是我需要的。
    • 我也使用这种方法,并且自从我使用它以来就已经这样做了。这在某种程度上取决于您对我发现的单元测试的定义; some.will 说因为你不直接调用任何其他 Java 对象测试的方法(这对控制器来说是可行的),你在单元测试和集成测试之间,但我发现通过这种方法来证明这种方法更容易从测试它的建议用法的角度来考虑它;控制器不像标准方法那样被调用,这只是对其实际使用的模拟,同时仍符合单元测试的核心原则
    猜你喜欢
    • 2019-09-20
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 2018-04-19
    相关资源
    最近更新 更多