1、提高单元测试效率
- 背景:在项目提测前,自己需要对代码逻辑进行验证,所以单元测试必不可少。但是现在的java项目几乎都是基于SpringBoot系列开发的,
所以在进行单元测试时,执行一个测试类就要启动springboot项目,加载上下文数据,每次执行一次测试都要再重新加载上下文环境,这样
就会很麻烦,浪费时间;在一次项目中,我们使用自己的技术框架进行开发,每次单元测试时都要初始化很多数据(例如根据数据模型建立表,
加载依赖其它模块的类),这样导致每一次单元测试时都会花3-5分钟时间(MacOs 四核Intel Core i5 内存:16g),所以很有必要
优化单元测试效率,节约开发时间。
2、单元测试如何执行
- 首先要优化单元测试,那要知道单元测试是怎样执行的
- 引入相关测试的maven依赖,例如junit,之后在测试方法加上@Test注解即可,在springboot项目测试中还需要在测试类加上@RunWith注解
然后允许需要测试的方法即可
补充说明:
@RunWith 就是一个运行器
@RunWith(JUnit4.class) 就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
@RunWith(Suite.class) 的话就是一套测试集合,
@ContextConfiguration Spring整合JUnit4测试时,使用注解引入多个配置文件@RunWith
SpringBoot环境下单元测试一般是加@RunWith(SpringJUnit4ClassRunner.class)注解,SpringJUnit4ClassRunner继承BlockJUnit4ClassRunner类,
然后在测试方式时会执行SpringJUnit4ClassRunner类的run方法(重写了BlockJUnit4ClassRunner的run方法),
run方法主要是初始化spring环境数据,与执行测试方法
3、项目中使用
- 在我们项目中,是通过一个RewriteSpringJUnit4ClassRunner类继承SpringJUnit4ClassRunner,然后@RunWith(RewriteSpringJUnit4ClassRunner.class)来初始化我们框架中需要的数据,
RewriteSpringJUnit4ClassRunner里面是通过重写withBefores方法,在withBefores方法中去初始化数据的,之后通过run方法最后代理执行测试方法
4、优化单测思路
- 通过上面说明,可以知道每次测试一个方法都要初始化springboot环境与加载自己框架的数据,所以有没有一种方式可以只需要初始化
一次数据,就可以反复运行测试的方法呢?
思路:首先每一次单测都需要重新加载数据,跑完一次程序就结束了,所以每次测试方法时都要重新加载数据,
如果只需要启动一次把环境数据都加载了,然后之后都单元测试方法都使用这个环境呢那不就能解决这个问题么。
我们是不是可以搞一个服务器,把基础环境与数据都加载进去,然后每次执行单元测试方法时,通过服务器代理去执行这个方法,不就可以了吗
5、实现方式
- 首先我们可以用springboot的方式启动一个服务,通常使用的内置tomcat作为服务启,之后暴露一个http接口,入参为需要执行的类和方法,
然后通过反射去执行这个方法;还可以通过启动jetty服务,通过jetty提供的handler处理器就可以处理请求,jetty相对于tomcat处理请求更加
方便 - 服务是有了,那怎样将单元测试方法代理给服务器呢?前面提到过,通过@RunWith注入的类,在单元测试方法运行时会执行@RunWith注入的类相应
的方法,所以我们可以在@RunWith注入的类里面做文章,拿到测试类与方法,然后通过http访问服务器,然后服务器去代理执行测试方法
6、编码实现
- 下面将通过两种不同方式实现,以Jetty为服务器启动,与以Tomcat为服务器启动
6.1 Jetty作为服务启动
- 首先编写服务启动类,并在spring容器准备好后加载我们公司框架相关数据,这里使用jetty作为服务器,下面代码是核心方法
// 只能写在测试目录下,因为写在应用程序目录下在序列化时,找不到测试目录下的类-》InvokeRequest类中的Class<?> testClass反序列化不出来
@SpringBootApplication
@ComponentScan(value = "包路径")
public class DebugRunner {
public static void main(String... args) {
SpringApplication.run(DebugRunner.class, args);
System.out.println("================================success========================");
}
@EventListener
public void onReady(ContextRefreshedEvent ev