关于 Mockito BDDMockito
前言
向很多开源社区提交代码是需要做完整的
单元测试
的,其中
mock
目标实例、给
mock
对象打桩 等操作可以极大提高测试代码的效率和可读性
本章节基于
Mockito
BDDMockito
的
API
DEMO
熟悉一些基本操作,
Spring Test
包含对应依赖
Mockito BDDMockito
其中
BDD
代表
behavior driven deployment
,我理解就是让测试代码语义更接近现实行为,比如
given就是给定 ... 条件下,可以类比为打桩(当然也可以不打)when就是发生 ... 行为,比如mock实例的方法调用(当然也可以不发生)then,就是则 ...,打桩的mock实例执行目标方法后的校验
语义化的东西强行解释总感觉生硬
mock
@TestpublicvoidmockDemo(){List mock =mock(List.class);
mock.add("test");
mock.add("test1");
mock.add("test1");
mock.get(0);// verify:是否调用目标方法verify(mock).add("test");// BDD stylethen(mock).should().add("test");// 目标方法调用一次verify(mock,times(1)).add("test");then(mock).should(times(1)).add("test");// 目标方法至少调用 2 次verify(mock,atLeast(2)).add("test1");then(mock).should(atLeast(2)).add("test1");// 目标方法最多调用 2 次verify(mock,atMost(2)).add("test1");then(mock).should(atMost(2)).add("test1");// 目标方法未调用verify(mock,never()).add("test2");then(mock).should(never()).add("test2");// errorverify(mock).add("test2");verify(mock).get(1);}
mock方法创建mock 对象,即一个临时虚拟对象,测试基于该示例进行verify是Mockito下的API,类似于断言,比如verify(mock).add("test")就是检验该方法是否调用then是BDDMockito的API,类比于verify更加语义化
stub
@TestpublicvoidstubDemo(){ArrayList mock =mock(ArrayList.class);// 打桩when(mock.get(0)).thenReturn("a");// BDD-stylegiven(mock.get(0)).willReturn("a");when(mock.get(1)).thenThrow(newRuntimeException("test"));// given(mock.get(1)).willThrow(new RuntimeException("test"));doThrow(newRuntimeException("void")).when(mock).clear();// willThrow(new RuntimeException("void")).given(mock).clear();System.out.println(mock.get(0));try{
mock.get(1);}catch(Exception e){System.out.println(e.getMessage());}System.out.println(mock.get(2));}
- 这是一段
stub代码,即打桩操作,主要应付方法逻辑复杂且非测试目标的场景 - 对应的
Mockito``````API是when ... then ... - 对应的
BDDMockito是then ... will ...
matcher
@TestpublicvoidmatcherDemo(){ArrayList mock =mock(ArrayList.class);// 匹配任意 intwhen(mock.get(anyInt())).thenReturn("meta");given(mock.get(anyInt())).willReturn("meta");// 所有的方法都要使用 matcherwhen(mock.subList(anyInt(),eq(1))).thenReturn(newArrayList(){{add("meta2");}});given(mock.subList(anyInt(),eq(1))).willReturn(newArrayList(){{add("meta2");}});System.out.println(mock.get(12));System.out.println(mock.subList(3,1));// then 阶段也可以使用verify(mock).get(anyInt());then(mock).should().get(anyInt());}
- 可以对参数进行匹配,比如
anyInt()匹配任意Integer - 这个针对
Mockito``````BDDMockito都一样 - 自然语义下的
then阶段也可以使用matcher来匹配
我指的自然语义就是 given ... when ... then ...
inorder
@TestpublicvoidorderDemo(){ArrayList mock =mock(ArrayList.class);LinkedList mock2 =mock(LinkedList.class);
mock.add(1);
mock.add(2);
mock2.add(1);
mock2.add(2);// 可以有多个实例InOrder inOrder =inOrder(mock, mock2);
inOrder.verify(mock).add(1);
inOrder.verify(mock).add(2);// correct,可以跳过// inOrder.verify(mock2).add(2);
inOrder.verify(mock2).add(1);// BDD-stylethen(mock2).should(inOrder).add(2);// error
inOrder.verify(mock).add(1);}
- 这段代码可以测试
mock实例的方法执行顺序是否符合预期 - 可以多个实例、多个方法结合使用,可看
demo理解 - 顺序前后符合即可,可以不严格,即
1 2 3 4可以是1 2 4、1 3 4但不可以2 1 3 4
consecutive
@TestpublicvoidconsecutiveDemo(){ArrayList mock =mock(ArrayList.class);when(mock.get(anyInt())).thenReturn(0,1,2);// 0System.out.println(mock.get(0));// 1System.out.println(mock.get(1));// 2System.out.println(mock.get(2));// 2System.out.println(mock.get(3));// 清空reset(mock);System.out.println(mock.get(0));}
打桩的结果可以指定多个,会依此返回- 如果没有其他结果,则保持最后一个
reset可情况mock实例在自然语义given和when阶段下的所有行为
answer
@TestpublicvoidanswerDemo(){ArrayList mock =mock(ArrayList.class);when(mock.get(anyInt())).then(invocation ->{Object argument = invocation.getArgument(0);return argument +"r";});// BDD-stylegiven(mock.get(anyInt())).will(invocationOnMock ->{Object argument = invocationOnMock.getArgument(0);return argument +"r";});System.out.println(mock.get(0));System.out.println(mock.get(1));}
打桩结果指定更加灵活的Answer- 示例中是基于
lambda风格的实现
spy
@TestpublicvoidspyDemo(){List list =newLinkedList();List spy =spy(list);
spy.add(1);when(spy.size()).thenReturn(10);// BDD-stylegiven(spy.size()).willReturn(10);System.out.println(spy.get(0));System.out.println(spy.size());// spy 只是一个 copy,所以 list 上的操作不影响 spy
list.add(2);
spy.forEach(System.out::println);}
spy方法可返回一个spy实例,该实例除了打桩行为外的调用都与原实例相同,比如示例中spy.add(1)方法就是正常的ArrayList::add,而spy.size()因为被stub只返回10- 值得注意的是,原对象上的操作并不影响
spy示例,比如示例中list.add(2)并不意味着spy.get(1) == 2
oneLinerStubs
@TestpublicvoidoneLinerStubsDemo(){HelloService helloService =when(mock(HelloService.class).hello()).thenReturn("hello world").getMock();// BDD-styleHelloService helloService2 =given(mock(HelloService.class).hello()).willReturn("hello world 2").getMock();System.out.println(helloService.hello());System.out.println(helloService2.hello());}
- 一行式
demo getMock方法返回mock实例
总结
本文介绍一些基于
Mockito
BDDMockito
的
DEMO
,旨在写出实用又好看的
单元测试
完整示例 demo
版权归原作者 小水牛... 所有, 如有侵权,请联系我们删除。