一、什么是jmockit?jmockit的优点?
1.为了让程序更加可靠,健壮,以及保证在项目重构时前后的业务逻辑保持一致。通俗说就是 尽量避免bug,从而需要编写单元测试。 2.一个好的单元测试是指:在能测试覆盖它所有的逻辑代码下,同时实现解耦&美观。 3.解耦的理想状态是:仅仅运行所编写的单元测试代码,就可以完成所有测试。(不依赖数据库, 不依赖第三方微服务模块)。 4.如果本项目测试的一个web接口调用了另一个微服务的业务接口,比如查数据库。那么为了完成 本项目的单元测试正常运行,就还需要启动另一个微服务,且另一个微服务的返回值是不可控的, 那么如何解决呢? 5.我们首先想到的是,如果不启动另一个微服务就可以拿到返回值情况下,那么就可以在本web接口中 将调用另一个微服务接口的地方直接将返回值写死。 6.这样做有什么坏处呢? 7.改变了本web接口的实际业务代码,增加了修改成本,如果逻辑不缜密,有可能与实际情况造成偏差。 8.那有没有不用改变本web接口的实际业务代码,就可以实现将另一一个微服务接口的返回值写死呢? 9.就是采用jmockit框架。 10.jmockit可以在本地模拟一个另一个微服务接口,从而达到本web接口不用真的去请求另一台微服务, 而是仅仅在本web服务就可以获得另一个微服务返回的数据,当然这个返回的数据是我们模拟的。 11.在通俗一点来说就是jmockit框架可以让我们自定义一个类,这个类去继承另一个微服务的接口方法, 从而重写这个方法,返回我们模拟的数据返回值。 12.选择jmockit还有原因,就是它基于java.lang.instrument包开发,并使用ASM 库来修改Java的Bytecode。 13.我们如果要知道一个类的类信息(类名,方法名,字段名),然后调用指定方法去运行,一般会通过反射的方法。 但是反射去加载对应的字节码文件,将其加载到JVM中,消耗了资源和cpu时间。而ASM是 直接读取的字节码文件,省去了加载的步骤,所以很快。 比如在Spring底层的@ConditionOnClass注解就采用的这个ASM
二、快速入门示例
首先引入需要的maven依赖(jmockit用来模拟,jmockit-coverage用来展示代码覆盖率)
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.36</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit-coverage</artifactId>
<version>1.23</version>
<scope>test</scope>
</dependency>
这里有二个踩坑点,1.这俩个依赖必须在junit之前声明引用。必须按照以下顺序
2.必须将jmockit加载到类路径中,(本次示例:${jmockit.version} = 1.36)
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<argLine>
-javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
</argLine>
</configuration>
</plugin>
建立一个我们需要测试的UserController接口。(这个接口会通过userId去另一个微服务查找该用户是否存在)
另一个微服务的接口:FindUserController.class(注意,这里并没写它的实现方法,在实际的微服务场景中,它的实现肯定在另一个服务器,所以本服务不管。)
public interface FindUserController {
boolean findByUserId(Integer userId);
}
建立一个我们要测试的接口:UserController.class(可以看到本接口就是调用的另一个微服务的接口)
@RestController
public class UserController {
@Autowired
@SuppressWarnings("all")
private FindUserController findUserController;
@RequestMapping("/checkUser")
public boolean isExistByUserId(@RequestParam("UserId") Integer userId){
boolean byUserId = findUserController.findByUserId(userId);
return byUserId;
}
}
编写单元测试
public class UserControllerTest {
@Injectable
private FindUserController findUserController;
@Tested(availableDuringSetup = true)
private UserController userController;
@Test
public void isExistByUserId(){
new Expectations(){
{
findUserController.findByUserId(5);
result = true;
times = 1;
}
};
new Expectations(){
{
findUserController.findByUserId(6);
result = false;
times = 1;
}
};
System.out.println(userController.isExistByUserId(5));
System.out.println(userController.isExistByUserId(6));
}
}
输出: 可以看到,我们设置了模拟的返回值,当userI的=5时就放回true,为6时就返回false。
入门到此结束。
|