1基本的使用步骤
1,环境使用mock和test包
2,使用mockitoRule构造mockito环境
@Rule
public MokitoRule rule = MockitoJUnit.rule();
注意这里的修饰符public,如果没有这个修饰符的话使用mock测试会报错
@Rule
public ExpectedException thrown = ExpectedException.none();
可以选择忽视抛出的异常?(我是这么理解的不知道是否正确)
3,对目标测试类加@InjectMocks注解
4,对目标测试类的依赖属性使用@mock创建依赖
5,使用@test注解测试目标类的方法执行逻辑
2mock对象创建和目标测试类的依赖注入
这里我还没有看juint执行的逻辑,只是看了mock环境下获取注解创建mock对象,并将mock的对向注入到@Injectmocks的目标测试对象中去的逻辑。
1,测试前的准备
使用的jar包版本是junit.junit.4.11,org.powermock.powermock-api-mockito.1.6.6
创建一个interface用于目标测试类的filed的type。
public interface MyInterface { public void show(); }
创建一个目标测试类,其中声明filed的type为上面的interface的type
public class DoMainObject {
private MyInterface a;
private MyInterface b;
public void aShow(){a.show();}
public void bShow(){b.show();}
}
最后创建一个测试用例对象用于测试目标测试类
public class MyMockTest {
@Rule public MockitoRule mockito = MockitoJUnit.rule();//创建mockito的rule
@Rule public ExpectedException thrown = ExpectedException.none();//忽视异常
@InjectMocks private DoMainObject doMainObject;//目标测试类
@Mock private MyInterface a;//目标测试类的field a
@Mock private MyInterface b;//目标测试类的filed b
@Test public void testShow(){
doMainObject.aShow();
doMainObject.bShow();
}
}
2进行debug测试
1测试类中加断点debug
我从rule对象执行的时候开始追踪,junit的运行原理略过
2,initmocks方法内部
其中的testClass就是测试用例,MyMockTest的实例,annotationEngine是默认的注解驱动InjectingAnnotationEngine,
这个方法的内部获取测试用例的type,获取注解驱动,并判断是否是默认的注解驱动,可以自定义注解驱动?(暂时我还办不到),之后注解驱动执行
3, InjectingAnnotationEngine .process方法
InjectingAnnotationEngine.process 内部只有两个方法,从名字和其上的注释可以知道
processIndependentAnnotations处理独立的filed,其实就是测试用例中有@mock注解的filed,这里就是a和b。processInjectMocks处理依赖于独立mock对象的filed,就是测试用例中有@InjectMocks注解的filed,依赖于mock对象的目标测试类,这里就是DoMainObject,先看processInjectMocks
4, processIndependentAnnotations
入参分别为测试用例的type,和instance,方法中只有一个循环,在循环的内部处理三件事
1delegate.process(classContext, testInstance);委派对象处理@Mock,@Captor等注解,
2spyAnnotationEngine.process(classContext, testInstance);监视注解驱动处理@Spy注解
3获取测试用例的父类,赋值给原来的变量
4如果Class的type为Object,跳出循环
这个方法就是先处理自己的独立注解,然后去处理父类的独立注解,如此往复直到父类为Object源类。
5delegate.process
这个方法参数还是Class的type和Class 的instance,
处理过程是获取instance的所有field就是所有的属性,然后循环获取filed的上的所有注解,更具注解和field尝试创建mock对象,这里最后的创建对象时使用cblib创建代理对象,最后创建一个Setter对象将创建的cglib代理对象mock对象,set进instance的field中去,即完成了一个测试用例中的属性的注入(spring的bean注解注入方式是不是也是如此呢,所有的基于注解的实现原理是否基本类似于此呢)
这里只关注两个方法createMockFor和FieldSetter(testInstance, field).set(mock)
6, DefaultAnnotationEngine. createMockFor
createMockFor方法的流程比较复杂,
这个方法的内部有两个方法,
DefaultAnnotationEngine.forAnnotation(),
在这个方法中对annotationd的类型与已有的注解处理器对象集合进行判断是否包含,如果包含取出对应的处理器对象,如果不包含空实现一个注解处理器实现process方法返回为null。也就是说在DefaultAnnotationEngine对象的实例中只处理特定的注解生成其mock代理对象。
这个注解处理器的集合是在4中创建了deletage时创建了DefaultAnnotationEngine对象,然后在其构造方法中调用了注册注解驱动方法
private final Map, FieldAnnotationProcessor?> annotationProcessorMap = new HashMap, FieldAnnotationProcessor>();
public DefaultAnnotationEngine() {
registerAnnotationProcessor(Mock.class, new MockAnnotationProcessor());
registerAnnotationProcessor(MockitoAnnotations.Mock.class, new MockitoAnnotationsMockAnnotationProcessor());
registerAnnotationProcessor(Captor.class, new CaptorAnnotationProcessor());
}
private void registerAnnotationProcessor(Class annotationClass, FieldAnnotationProcessor fieldAnnotationProcessor) {
annotationProcessorMap.put(annotationClass, fieldAnnotationProcessor);
}
DefaultAnnotationEngine.process()
根据获取的annotationProcess对象执行process方法,如果不是从map中获取的那么返回值就是null,在本测试中就是@mock的方法返回了MockAnnotationProcessor类型的注解驱动,
Mockito.mock(field.getType(), mockSettings);这个就是最后创建mock的cglib代理对象的方法,对这个方法暂时就不继续追踪了,
小结
现在我们已经将一个@Mock注解下的测试类中的field建立好了,让我们回到5的process方法中,能看见这个步骤就是重复的执行这段逻辑:
获取field的所有注解,调用createMockFor方法,然后在此方法中和DefaultAnnotationEngine预置的FieldAnnotationProcessor 实现类型集合做匹配,满足的获取指定的注解处理器创建对应的mock对象。不满足的创建一个匿名子类,其中实现的方法指定返回null。以此将@Mock等注解和其他注解区分开来,只创建@Mock和@Captor等独立的注解。如此步骤processIndependentAnnotations.process()就完成了。
spyAnnotationEngine.process的执行类似,但是这个注解处理类是使用反射去根据类型创建一个真实的实例对象返回而不是创建一个mock的cglib对象。
7,InjectingAnnotationEngine .processInjectMocks
现在我们完成了processIndependentAnnotations,来看看现在的测试用例instance
可以看到现在a,b使用@Mock注解的field已经存在cglib的代理对象了,使用@InjectMocks的doMainObject暂时还是null,现在来看processInjectMocks
方法内部的核心方法是injectMocks,内部的逻辑从子类到父类最后到Object处理每个继承层级的@InjectMocks注解
8 .InjectingAnnotationEngine.injectMocks
方法内处理
1获取测试用例的Class类型
2创建一个Field对象的set集合
3循环处理
4将class中所有InjectMocks注解的field放到mockDependentFields集合中
5将创建的mock对象添加到mocks集合中
6Class类型是Object跳出循环
7创建@InjectMock注解修饰的field
9DefaultInjectionEngine.injectMocksOnFields
这是根据依赖创建目标测试类的mock对象。
最后方法完成的时候
可以看到目标测试类创建完成,依赖a,b也已经注入。
3,总结
没有去追踪Junit和cglib只是将中间mock的注解的过程进行了追踪:
基本就是先创建mock对象,然后将根据依赖创建@InjectMocks的目标测试类对象
其中注解的区分
1@InjectMocks和其他类型不同,
2@Spy和@Mock,@Captor等注解不同
4,注意
在使用的过程中Rule一定要是pulic修饰的
在使用mockito的时候还出现了mock的对象在测试的时候报空指针的问题,我追踪后发现是同类型的interface在注入@InjectMocks修饰的主目标对象的时候是有排序的,会根据测试类中的filedName进行排序依次向下注入,解决办法就是把@InjectMocks的所有field都进行mock。