Java 是否可以在没有@DirtiesContext的情况下测试Spring REST控制器?

Java 是否可以在没有@DirtiesContext的情况下测试Spring REST控制器?,java,spring,rest,testing,spring-boot,Java,Spring,Rest,Testing,Spring Boot,我正在开发一个SpringBootWeb应用程序。编写集成测试的常用方法是: @Test @Transactional @Rollback(true) public void myTest() { // ... } 只要只有一个线程工作,这就可以很好地工作@Rollback如果存在多个线程,则无法工作 但是,当使用Spring REST模板测试@RestController类时,总是有多个线程(按设计): 充当客户端并运行REST模板的测试线程 接收和处理请求的服务器线程 因此,您

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

@Test
@Transactional
@Rollback(true)
public void myTest() {
    // ...
}
只要只有一个线程工作,这就可以很好地工作<代码>@Rollback如果存在多个线程,则无法工作

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

  • 充当客户端并运行REST模板的测试线程
  • 接收和处理请求的服务器线程
因此,您不能在REST测试中使用
@Rollback
。问题是:您使用什么来替代测试,使测试可重复并在测试套件中很好地发挥作用?


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

首先,使用Spring上下文测试控制器不是单元测试。您应该考虑为控制器编写一个单元测试,使用依赖关系的MOCK并创建一个:

如果只需在MVC builder上调用
setControllerAdvice()
,就可以使用外部
@ControllerAdvice
进行错误处理等操作,这甚至可以实现

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


您描述的部分集成测试也有助于确保使用正确的接线,并确保所有测试单元按预期协同工作。但我更倾向于进行更通用的集成测试,包括多个/所有端点检查它们是否正常工作(不检查边缘情况),以及仅模拟外部服务(如内部REST客户端,用内存中的一个替换数据库,…)。使用此设置,您可以从一个新的数据库开始,甚至可能不需要回滚任何事务。当然,使用数据库迁移框架(如动态设置内存中的db)是最合适的。首先,使用Spring上下文测试控制器不是单元测试。您应该考虑为控制器编写一个单元测试,使用依赖关系的MOCK并创建一个:

如果只需在MVC builder上调用
setControllerAdvice()
,就可以使用外部
@ControllerAdvice
进行错误处理等操作,这甚至可以实现

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


您描述的部分集成测试也有助于确保使用正确的接线,并确保所有测试单元按预期协同工作。但我更倾向于进行更通用的集成测试,包括多个/所有端点检查它们是否正常工作(不检查边缘情况),以及仅模拟外部服务(如内部REST客户端,用内存中的一个替换数据库,…)。使用此设置,您可以从一个新的数据库开始,甚至可能不需要回滚任何事务。当然,使用数据库迁移框架(如动态设置内存中的db)是最合适的。因此,基本上您要做的是直接调用REST控制器方法,而不涉及应用程序容器(嵌入式或其他),对吗?这是一个有趣的方法,谢谢你的指点。那么您要单独测试“实体到json”的映射吗?您是否对整个应用程序使用更大的、基于场景的集成测试?@Alan47否,在单元测试中使用模拟mvc时,您还将测试所有映射,而不会直接调用控制器方法。相反,您可以使用mvc.perform创建一个模拟请求,该请求映射到控制器,包括任何(默认)转换和(默认)消息转换器。如果您需要特殊的东西,您必须决定不在这里测试或进行更复杂的MVC构建(例如添加HAL支持或特殊转换服务设置)。听起来很棒!我需要在spring文档中进一步研究这个MockMVC。谢谢我刚刚在我的应用程序中尝试了MockMVC,它工作起来很有魅力,它非常棒,比SpringREST模板好得多。谢谢你向我指出这一点,我总是忽略它,因为“MVC”部分(我不使用spring webmvc),所以我认为这不是我需要的。我经常使用这种方法,自从我发现它以来,我就一直这样做。这在某种程度上取决于我发现的单元测试的定义;some.will会说,因为你不像其他Java对象测试那样直接调用这些方法(这对控制器来说是可行的),你是介于单元测试和集成测试之间的,但我发现,通过测试它的建议用途来证明这种方法更容易;控制器并不像标准方法那样被调用,这只是模拟它的实际使用,同时仍然与单元测试的核心原则保持一致。因此,基本上,您要做的是直接调用REST控制器方法,而不涉及应用程序容器(嵌入式或其他),对吗?这是一个有趣的方法,谢谢你的指点。那么您要单独测试“实体到json”的映射吗?您是否对整个应用程序使用更大的、基于场景的集成测试?@Alan47否,在单元测试中使用模拟mvc时,您还将测试所有映射,而不会直接调用控制器方法。相反,您可以使用mvc.perform创建一个模拟请求,该请求映射到控制器,包括任何(默认)转换和(默认)消息转换器。如果您需要特殊的东西,您必须决定不在这里测试或进行更复杂的MVC构建(例如添加HAL支持或特殊转换服务设置)。听起来很棒!我需要在spring文档中进一步研究这个MockMVC。T
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()
  }
}