mock单元测试 mockito实践

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。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,656评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,114评论 25 707
  • 木工坊给孩子们开辟了一个全新的集游戏、手工操作为一体的专业活动场所。我们从认识工具、讲解工具的使用方法开...
    四叶草扫地僧阅读 1,097评论 0 2
  • 戒烟对于身体的健康是必须的。但为什么我们的身边总有人戒不了烟呢? 有一种说法,说是身体的细胞对烟草燃烧后产生的有害...
    舒云萱阅读 1,221评论 1 1
  • Part1. “不要什么都管着我了!我已经长大了!不是过去的小女孩了!”继近几个月来不停的小摩擦和小争吵过后,十七...
    math_soccer阅读 279评论 0 1