项目里的UT越来越慢,怎么办?
JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。
它已经发展有20余年历史了
现在我们的项目在Jenkins流水线上每次部署时,随着code越来越多,UnitTest这部分Stage也每次都跑得最慢,怎么可以加快速度呢?
可以试着从code层面去尝试 refactor,比如将Junit4升级到更好效率更快的Junit5
JUnit5
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JDK Version >= 8 (而 JUnit4是要求 JDK Version >= 5)
JUnit平台 | 它定义了TestEngine用于开发在平台上运行的新测试框架的API |
---|
JUnit Jupiter | 它具有所有新的junit注释和TestEngine实现,以运行使用这些注释编写的测试 | JUnit Vintage | 支持在JUnit 5平台上运行JUnit 3和JUnit 4编写的测试 |
JUnit5的标准用法
-
集成Spring(The SpringJUnitConfig and SpringJUnitWebConfig Annotations in Spring 5 | Baeldung) -
使用JUnit5的拓展模型特性,@ExtendWith(SpringExtension.class),可以使用Spring的上下文装载功能,而不用去使用重量级的@SpringBootTest -
集成Mockito (Mockito and JUnit 5 - Using ExtendWith | Baeldung) -
引入JUnit5和Mockito的maven依赖 -
配置Surefire Plugin,使得测试代码都运行在新的JUnit平台上 -
有必要兼容JUnit4的测试代码的话,还需加入JUnit5 vintage-engine的maven依赖 -
@ExtendWith(MockitoExtension.class), 集成Mockito,可以使用@Mock, @Spy, @InjectMocks等注解,方便我们写UT
JUnit 4 VS JUnit 5
- 注解的区别
JUnit4 | JUnit5 |
---|
@Before | @BeforeEach | @After | @AfterEach | @BeforeClass | @BeforeAll | @AfterClass | @AfterAll | @Ignore | @Disabled | | @TestFactory | | @Nested | @Category | @Tag | | @ExtendWith |
- 注解的使用区别
2.1. 常用的
@Test(expected = Exception.class)
@Test(timeout = 1)
Assertions.assertThrows();
Assertions.assertTimeout(1);
2.2. @RunWith 和 @ExtendWith(最大特性差异)
@RunWith 用于将测试上下文与其他框架集成或更改 JUnit 4 中测试用例中的整体执行流程
而在 JUnit 5 中,我们现在可以使用 @ExtendWith 注释来提供类似的功能
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringExtensionTest {
}
@ExtendWith(SpringExtension.class)
public class SpringExtensionTest {
}
为什么要使用JUnit5
- 粒度更细
JUnit 4 有一些明显的限制:整个框架包含在一个 jar 库中,即使只需要特定功能,也需要导入整个库
而在 JUnit 5 中,我们获得了更多的粒度,并且可以只导入必要的内容
- 同时运行多个运行器
一个测试运行器一次只能在 JUnit 4 中执行测试(例SpringJUnit4ClassRunner 或 Parameterized )
而在 JUnit 5 允许多个运行器同时工作
- 赶上JDK8的浪潮
这个大家写过Java的,懂得都懂
升级
- 将@SpringBootTest 以及 @RunWith(SpringRunner.class 或者 SpringJUnit4ClassRunner)移除,换成JUnit5拓展新特性 @ExtendWith(SpringExtension.class)
Spring Boot 提供了@SpringBootTest 注解,我们可以使用它来创建一个应用程序上下文,其中包含所有上述测试类型所需的所有对象。但是请注意,过度使用@SpringBootTest 可能会导致测试套件运行时间过长
SpringRunner 继承自 SpringJUnit4ClassRunner
- 将所有org.junit.的 import,都换成 org.junit.jupiter.
(Migrating from JUnit 4 to JUnit 5 | Baeldung)
优化思路
-
多使用Mockito轻量级的工具 去写UT@ExtendWith(MockitoExtension.class) 需要用到Spring Context,就可使用 @ExtendWith(SpringExtension.class) -
在代码层面可以将 @Autowired换成,构造器注入,与Spring解耦合,不然写UT需要注入Bean的时候,就不得不使用Spring容器 -
少使用@MockBean,@SpyBean这些Spring提供的注释,虽然它可以帮我们轻松使用 Mockito 的Mock功能。让Spring Boot 功能很容易包含在我们的测试中,但我们应该意识到成本:每个测试都可能创建一个新的应用程序上下文,这可能会显着增加测试套件的运行时间,而这恰恰也就是你UT跑得慢的原因! -
除了在写集成测试时用到@SpringBootTest,其他均不用,不得已要使用的话,也尽可能尽着 少加载Bean的目标,去写code,越少bean加载,速度就越快
- classes 指定需要 装载的 class
- webEnvironment不使用时关掉(不过好像默认是关的)
- 升级JUnit5
说在最后
UT这种东西,当然是覆盖率和质量越高越好,但也取决于项目的赶工程度,又想马儿跑,有不给马儿吃草,这种本就不现实
但作为开发人员,大家要对技术有一定了解,不要老是copy前人代码的改改改,因为别人的不一定是对的,也不一定是最好的,只能说明,它是稳定且不怎么出大问题的
最后 好文推荐:
|