关于 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
版权归原作者 小水牛... 所有, 如有侵权,请联系我们删除。