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

时间:2017-12-14 08:36:43

标签: java spring rest testing spring-boot

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

Schema.Field field = new Schema.Field("new_field",SchemaBuilder.builder().intType(),
    "NewField", 10);

List<Schema.Field> fields = new ArrayList<>();

for (Schema.Field f : oldSchema.getFields()) {

   Schema.Field _field = new Schema.Field(f.name(), f.schema(), f.doc(), f.defaultValue());
  fields.add(_field);

}

只要一个线程完成工作,这就可以正常工作。如果有多个线程,则@Test @Transactional @Rollback(true) public void myTest() { // ... } 无效。

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

  • 充当客户端并运行REST模板的测试线程
  • 接收并处理请求的服务器线程

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

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

1 个答案:

答案 0 :(得分:4)

首先,使用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()
  }
}

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

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

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