SpringBoot-Testing
在写上一篇IoC
和DI
之前,本来想记录的是SpringBoot
如何完成AutoConfiguration
自动配置,但因为篇幅直接分离出来了一篇。这次又把Testing
给分离了出来。
事情的起因是,写项目代码时,常常需要对自己写的部分进行测试和调试。根据自己以往的编程经验,个人习惯于写一点就运行一点,并通过断点调试来确认内存的运行状态,避免犯一些低级的逻辑错误。虽然公司有专门的测试岗位,但从代码提交到GitLab请求合并,到部署到测试服务器,到测试人员设计用例并测试,到反馈到开发人员,整个过程涉及多个人员,太过漫长。而一些代码本身就需要反复调试,例如写了一些数据库操作,需要确认数据库实际执行结果是否符合预期。例如一个循环的跳出是否符合预期。执行结果符合预期本身是个很玄乎的事,很多时候代码看起来没啥问题,跑起来却又满脸问号。询问同事,都回答说让测试去测。而我觉得开发人员有义务确保代码没有低级的逻辑错误。
JUnit
在SpringBoot
框架中,常用的测试框架是JUnit
。现在最新版本SpringBoot3.0.5
对应的Junit5
版本,官方文档。公司内部使用的是Junit4
。
对于JUnit
框架内部原理暂时没深入了解。从使用角度,常常会对需要测试的类,新生成一个对应的测试类,命名规范通常为
新测试类命 = 旧类命 + "Test"
。例如有一个Calculator
类,那会新定义一个CalculatorTest
测试类,并在测试类中定义成员测试方法,对应于被测试类中的方法。在每个成员方法中添加注解@Test
,通过IDE或者构建工具就可以自动执行测试方法。
1 |
|
在测试中,可以通过构建测试用例,并使用断言,将方法输出和预期输出进行对比。还可以使用代码覆盖率工具,来分析测试覆盖率。这些是JUnit
核心功能,其他功能暂时还未深入了解。
Testing in SpringBoot
在SpringBoot
中,一个应用有什么功能,取决于在容器中有哪些Bean
实例。例如我需要访问数据库,容器中必须要有JDBC
相关的Bean
,我需要处理web
请求,容器中必须有Tomcat
和Sevlet
相关的Bean
。
如果采用上文的方式去测试Spring
项目中某个模块,那必然会报错。因为JUnit
并不会像Spring
一样启动,并将这些功能Bean
添加到容器中。也就无法完成例如依赖注入等功能。
为了让JUnit
能启动容器环境,Spring
提供了@SpringBootTest
注解。只需要在测试类前添加@SpringBootTest
,就可以在测试时启动Spring
容器。如果不指定注解的classes
属性,也就是启动的配置类,那么会将主应用程序类,也就带有@SpringBootApplication
注解的类作为配置类。
另外,如果使用的是JUnit4
,还需要在测试类上额外添加@RunWith(SpringRunner.class)
。需要这么做的原因涉及JUnit
框架原理,在此不深究。
但问题是,使用@SpringBootTest
注解时,会创建一个真实的SpringBoot
上下文,并加载所有的Bean
,这会导致测试的运行速度较慢。测试可能往往只是想测一个刚写完的某个方法,但却需要将许多不相关的Bean
都加载进来,这会造成严重性能浪费。较长的测试启动时间也会大大降低测试时的主观感受。
要解决这个问题,就需要知道容器里的Bean
是怎么样的方式被加载进去的,已经如何自定义加载需要的Bean
,这也是下一篇要讨论的内容。
总结
JUnit
是Java
中最公认的测试框架。使用时只需要在测试类的测试方法上加@Test
注解,即可对目标方法进行测试。在测试类中,可以使用许多功能并生成测试报告,来判断目标方法是否按照预期执行。
由于JUnit
框架默认不会启动Spring
容器,而Spring
应用的功能依赖于Spring
容器。因此直接使用JUnit
测试Spring
项目中的类会发生错误。可以使用@SpringBootTest
在测试类中启动Spring
容器环境。