缓慢的SpringBootTest


我们大多数人都面临着毫无疑问的信念。这样的想法可以从日常琐事,如杂货店购物到严重的宗教事务,一应俱全。就我而言,这是一种专业信念,特别是集成测试是否太慢。

我们的团队(以及我们的章节)相信集成测试的速度很慢,并且这种信念使我们没有在技术债务积压中解决这个问题,我们都接受了这一事实。一直持续到某些微服务的构建时间超过10分钟,并且队友开始抱怨它们。第一个反应是尽可能避免集成测试,而改用单元测试。我知道,有很多争论不是一个明智的决定,但这是团队做出的决定。在这篇文章中,我们将看到如何解决此问题并将微服务的构建时间减少一半。

最终,在一个周末内,我决定关注此问题并找到以下问题的答案:为什么我们的集成测试这么慢?我从写下所有我怀疑的东西开始,但是我的脑子只被@SpringBootTest迷住了。

Following the First Suspicion 每当我们运行测试时,我都会看到Spring徽标多次出现。我认为@SpringBootTest为每个测试类加载了完整的应用程序上下文。经过十分钟的搜索,我意识到我所有的假设都是错误的。我在Spring文档中找到了重点:

一旦TestContext框架为测试加载了ApplicationContext(或WebApplicationContext),该上下文将被缓存并重新用于在同一测试套件中声明相同唯一上下文配置的所有后续测试。

那么,为什么仍要为每个测试类加载上下文呢?实际上,事实并非如此。我是通过计算日志中特定文本的上下文负载来获得此信息的(每次Spring加载应用程序上下文时都会重复此操作)。像这样的东西:

mvn clean install > build-log.txt
grep "The following profiles are active: test" log.txt| wc -l

结果是16,而我们在代码库中有46个集成测试类。这意味着它没有为每个测试类加载上下文,但是为什么仅加载16次?

深入Test Classes 通过计算上下文负载获得了这个奇怪的结果之后,我一个接一个地检查了所有测试类以找到线索。我意识到,所有集成测试类都由@TestPropertySource注释,以加载一个或多个特定的属性文件。在此调查中,我还发现了另一个奇怪的发现:集成测试中的MockBean和SpyBean批注。在集成测试中使用这些批注不但引起了哲学上的问题,而且还大喊:“嘿,伙计,这里有代码的味道”。

我开始了第二轮搜索,与上一轮一样,我仅在十分钟后就找到了大量与上下文相关的有用文章和博客文章以及一些线索。我发现这是很有帮助的博客文章由何塞·卡洛斯·瓦莱罗桑切斯有关优化春季启动集成测试,这一次,它的作者已经有同样的旅程我的。

通过阅读这些文章和相同的文章,我意识到集成测试中存在一些陷阱,这些缺陷会阻止Spring在集成测试中重用已加载的应用程序上下文。这里是最重要的:

  • Using @MockBean and @SpyBean
  • Using@DirtiesContext
  • Careless use of profiles in integration tests
  • Careless use of @TestPropertySource

我将在单独的博客文章中详细审查所有这些项目。现在,让我们看看我如何通过两个小动作来增强集成测试。

优化集成测试 如前所述,我们的集成测试课程有两个陷阱。第一个是使用@TestPropertySource注释所有集成测试。我快速浏览了一下,发现每个类使用不同的属性文件组合。例如:

@TestPropertySource({
    "classpath:x.properties",
    "classpath:y.properties"
})
public class TestOne{
    //...
}
@TestPropertySource({
    "classpath:z.properties",
    "classpath:y.properties"
})

public class TestTwo{
    //...

}
@TestPropertySource({
    "classpath:x.properties",
    "classpath:z.properties"
})
public class TestThree{
    //...
}

@TestPropertySource({
    "classpath:x.properties",
    "classpath:z.properties"
})
public class TestFour{
    //...
}

在这种情况下,Spring将按属性文件的每个唯一组合加载上下文。例如,在前面的示例中,它两次加载了上下文,因为x和z重复了两次,因此Spring可以为它们两个重用上下文。

因此,我决定从所有集成测试类中删除TestPropertySource批注,并将它们聚合到所有它们都扩展的抽象类中。这是测试类的新样式:

@TestPropertySource({
    "classpath:x.properties",
    "classpath:y.properties",
    "classpath:z.properties"
})
public abstract class AbstractTes{
    //...
}

public class TestX extends AbstractTest{
    //...
}

现在,在此示例中,它仅加载一次上下文。我在代码库中做了类似的事情,并再次计算了上下文的负载。结果令人鼓舞:上下文负载的数量从16个减少到2个。请注意,例如,如果在x.properties和z.properties中都具有一个名为database.url的属性,则第二个属性将覆盖第一个属性。

我完成了其余的测试课程。你猜怎么了?它们都包含@MockBean或@SpyBean。如前所述,在另一篇博客文章中,我将介绍这些奇特的注释的用法以及它们在集成测试中可能引起的问题。

学过的知识 整个过程大约需要3到4个小时。因此,Jenkins的构建时间从10分钟减少到4分钟。这意味着有时我们可以通过花一些时间来解决这些长期浪费大量时间的问题,从而节省大量时间。


原文链接:http://codingdict.com