引入JMockit
项目pom文件中添加插件配置
<plugin>
<groupId>com.ctrip.flight.testframework.tools</groupId>
<artifactId>ut-maven-plugin</artifactId>
<version>2.2.4</version>
<executions>
<execution>
<phase>none</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
引入依赖(顺序不要乱)
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.37</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
API
JMockit有两套API,MockUp和Expectations
new Expectations() {
{
instance.XXX();
result = T;
}
}
MockUp
基本用法:
new MockUp<T>() {
@Mock
Object XXXXX() {
return obj
}
}
示例:
现有interface IDepend:
public interface IDepend {
String getName(Object obj);
void doSomething(Object obj1,Object obj2);
}
实现类A:
public class DependA implements IDepend {
@Override
public String getName(Object obj) {
return "A";
}
@Override
public void doSomething(Object obj1, Object obj2) {
System.out.println("A Do Something");
}
}
实现类B:
public class DependB implements IDepend {
@Override
public String getName(Object obj) {
return "B";
}
@Override
public void doSomething(Object obj1, Object obj2) {
System.out.println("B Do Something");
}
}
待测试类:
public class Service {
private IDepend dependA = new DependA();
public String serviceGetName() {
doSamething();
return dependA.getName(new Object());
}
private void serviceDoSamething() {
dependA.doSomething(new Object(), new Object());
}
}
1.Service mock 自身serviceDoSamething
new MockUp<Service>() {
@Mock
void serviceDoSamething() {
}
}
2.Service mock DependA的doSomething/getName
new MockUp<DependA>() {
@Mock
String getName(Object obj){
// 当代码执行到dependA.getName(new Object())时,返回值为"Mock"
return "Mock";
}
@Mock
void serviceDoSamething() {
}
}
3.Mock继承自IDepend的所有实现类DependA,DependB
public static <T extends IDepend> void doMock() {
new MockUp<T>() {
@Mock
String getName(Object obj){
// 可以根据入参obj的不同,返回不同的结果
if (obj == null ) {
return "Mock A";
}
else return "Mock B";
}
@Mock
void serviceDoSamething() {
}
};
}
4.MockUP支持mock外部/自身的public、protected、private、static 方法,用法同上,使用方法签名+@Mock注解。
5.Mock 构造函数
class T{
public T(){
System.out.println("init1");
System.out.println("init2");
System.out.println("init3");
}
}
执行Mock:
new MockUp<T>{
@Mock
void $init(){
// $init会对T的构造器进行拦截,原有构造器执行3次打印,拦截之后,只会执行一次"init"的打印
System.out.println("init");
}
}
6.模拟AOP操作
new MockUp<T>{
@Mock
Object $advice(Invocation invocation) {
// $advice 会拦截所有请求
System.out.println("执行AOP逻辑");
// 继续调用方法
return invocation.proceed()
}
}
7.MockUp实现原理
基于JVM的动态加载机制(java 版本需>=1.6),通过改动原有Class对象的二进制码,重新虚拟出一个Class对象,等于在源码层面,将方法进行了替换。
Expectations
MockUP功能非常强大,支持几乎日常工作中所有可能的场景,但是当我们的代码发生变动,如新增一个参数,方法名发生变动等,原有的测试代码无法感知到,因方法签名无法匹配上最终倒是UT执行失败。
现有待mock类DependA:
class DependA{
public String test(int index, Object obj){
XXX;
}
}
1.Mock 类/实例
- mock Class(类的所有实例都会被Mock)
@Mocked
DependA instance ; //此处不使用@Mocked,直接new 一个实例也是可以的
new Expectations(DependA.class){
instance.test(anyint , withInstance(DependA));
}
- mock 实例(只mock类某一个实例,其他实例不会进入mock)
@Mocked
DependA instance ; //此处不使用@Mocked,直接new 一个实例也是可以的
new Expectations(instance){
instance.test(anyint , withInstance(DependA));
}
说明:上述两种情况,一种Expectations的传入参数是Class,一个是Class的具体实例。
test方法的入参,anyint, anyString, (Class)any,withInstance(Class)…均来自于Expectations的父类Invocations(抽象类),其中定义了成员变量以及方法
2.Mock 根据条件返回不同结果
关键字:Delegate
需求:需要根据入参index返回不同值,如index=0,返回null,其他返回"test"
@Mocked
DependA instance ;
new Expectations(VisaActionImpl.class) {
{
testInstance.test(anyInt, (Object) any);
result = new Delegate() {
String testMock(int index, Object obj) { // 注意此处自定义的deleage方法名称,不需要与被代理方法名称一致,但是参数必须一致
return index == 0 ? null : "test"; //当测试用例index,传参为0时,DependA的test方法返回值即为null,其他情况则返回“test”
}
};
}
};
3.Mock 接口
现有接口IDepend:
public interface Idepend {
public String test();
}
现需对所有Idepend的实例的test方法进行mock:
@Capturing
Idepend testInstance;
new Expectations(){
testInstance.test();
result = "test";
}
4.Returns
关键字Returns可接收一组数据,可按照条用次序依次返回结果
new Expectations(){
testInstance.test();
Returns("1","2","3");
}
当test方法连续调用三次,三次mock的返回值,依次为"1",“2”,“3”
参考文档
http://jmockit.cn