Java spring boot应用程序中缓慢的单元测试

Java spring boot应用程序中缓慢的单元测试,java,spring,maven,unit-testing,Java,Spring,Maven,Unit Testing,我对单元测试还是新手。我开始读一本关于它的书。但最重要的一点是测试必须是第一位的(快速、隔离、可重复、自我验证、及时) 好了,现在我准备好练习了。但当我在SpringBoot中构建单元测试时。我喜欢把它们分开 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = UnitTestApplication.class, loader = SpringApplicationContextLoader.class)

我对单元测试还是新手。我开始读一本关于它的书。但最重要的一点是测试必须是第一位的(快速、隔离、可重复、自我验证、及时)

好了,现在我准备好练习了。但当我在SpringBoot中构建单元测试时。我喜欢把它们分开

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = UnitTestApplication.class, loader = SpringApplicationContextLoader.class)
@WebIntegrationTest("server.port:9000")
public class FirstTestClassTest{

    ...

}


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = UnitTestApplication.class, loader = SpringApplicationContextLoader.class)
@WebIntegrationTest("server.port:9001")
public class SecondTestClassTest{

    ...

}
这给了我一个问题,即对于每个测试类,都有一个新的应用程序实例启动

假设我引入了一个新特性,并想测试bug。我从命令行使用mvn测试。然后所有的测试都会运行,但我认为对于一个有大量测试的实际应用程序来说,这将需要很长时间


我有一种方法,只有一个实例启动并保持快速测试,但是我可以在哪里将测试保持在单独的类中

使spring mvc测试更快的方法之一是使用
MockMvcBuilders.standaloneSetup
在这个设置中,您使用的不是真实的而是模拟的应用程序上下文。这个设置有一个非常复杂的过程,根据我的经验,它对于大多数控制器测试都是足够的

这样,您需要创建控制器实例,并手动向它们注入模拟依赖项。下面是一个带有独立设置的MockMvc单元测试示例

// setup beans and mockMvc
UserDAO userDAO = mock(UserDAO.class);
UserController userController = new UserController(userDAO);
MockMvc mockMvc = standaloneSetup(userController)
             .setViewResolvers(new WebConfig().viewResolver())
             .build();
...

// do test
mockMvc.perform(get(USERS))
            .andExpect(view().name(USERS_VIEW_NAME))
            .andExpect(model().attribute(TOTAL, 20))

我会参考你的句子:

假设我引入了一个新特性,并想测试bug。我 从命令行使用mvn测试。然后所有的测试都运行了,但我 我认为一个需要大量测试的真正应用程序需要花费很多时间

您在这里提出的这个问题非常重要,因此我将尝试在这里提供一个体系结构的观点,而不深入研究技术实现

有许多不同类型的测试。您所指的是集成测试,而不是单元测试。这两者之间的区别在于,在单元测试中,您测试的是一个类,其余的所有类都应该被模拟(有EasyMock或Mockito这样的框架)

单元测试有很多限制:

  • 您无法访问任何外部组件(web服务器、数据库等)
  • 单元测试只在内存中运行
  • 它们非常快(秒粒子)
在测试类的功能时,这些测试非常有用

现在,当特性的开发完成后,是时候测试特性是否在“半真实”环境中工作了。通常在运行这些程序时,除了spring容器之外,还有一个可用的数据库。为了运行此类测试,您可以使用spring测试扩展并使用不同的技术(例如,用“部分”上下文替换整个应用程序spring上下文,或者用存根或其他内容覆盖某些bean)

现在,在你的问题中,这种差异很有趣,因为应用程序中应该有许多单元测试,而集成测试却要少得多。因此,如果你能用单元测试涵盖你所写的东西,那么就编写单元测试。它的速度快得令人难以置信,而且你可以运行很多(数千个)很快。只有当您(很少)无法在单元测试中涵盖您的功能时,才编写集成测试。这通常与针对DB运行的代码相关,例如,您希望测试组件生成的查询是否真的有效


因此,通常情况下,如果您同时拥有这两种类型的测试,那么应该涵盖它们,并且不应该有太多的集成测试,因此运行这些测试应该不是一件真正的麻烦事。

对于如何正确调用此类测试,有几种意见。我称它们为集成测试(正如注释
@WebIntegrationTest
所建议的那样),因为即使只测试一个特定的方法,也会运行整个应用程序上下文。而不是编写真正的单元测试并模拟类依赖项。然后,测试将在几毫秒内运行。