云存储能用来做网站吗/网站推广方案有哪些
关于 Mockito BDDMockito
- 前言
- Mockito BDDMockito
- mock
- stub
- matcher
- inorder
- consecutive
- answer
- spy
- oneLinerStubs
- 总结
前言
向很多开源社区提交代码是需要做完整的 单元测试
的,其中 mock
目标实例、给 mock
对象打桩 等操作可以极大提高测试代码的效率和可读性
本章节基于 Mockito
BDDMockito
的 API
DEMO
熟悉一些基本操作,Spring Test
包含对应依赖
Mockito BDDMockito
其中 BDD
代表 behavior driven deployment
,我理解就是让测试代码语义更接近现实行为,比如
given
就是给定 ... 条件下
,可以类比为打桩
(当然也可以不打)when
就是发生 ... 行为
,比如mock
实例的方法调用(当然也可以不发生)then
,就是则 ...
,打桩
的mock
实例执行目标方法后的校验
语义化的东西强行解释总感觉生硬
mock
@Testpublic void mockDemo() {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
@Testpublic void stubDemo() {ArrayList mock = mock(ArrayList.class);// 打桩when(mock.get(0)).thenReturn("a");// BDD-stylegiven(mock.get(0)).willReturn("a");when(mock.get(1)).thenThrow(new RuntimeException("test"));// given(mock.get(1)).willThrow(new RuntimeException("test"));doThrow(new RuntimeException("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
@Testpublic void matcherDemo() {ArrayList mock = mock(ArrayList.class);// 匹配任意 intwhen(mock.get(anyInt())).thenReturn("meta");given(mock.get(anyInt())).willReturn("meta");// 所有的方法都要使用 matcherwhen(mock.subList(anyInt(), eq(1))).thenReturn(new ArrayList() {{add("meta2");}});given(mock.subList(anyInt(), eq(1))).willReturn(new ArrayList() {{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
@Testpublic void orderDemo() {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);// errorinOrder.verify(mock).add(1);}
- 这段代码可以测试
mock
实例的方法执行顺序是否符合预期 - 可以多个实例、多个方法结合使用,可看
demo
理解 - 顺序前后符合即可,可以不严格,即
1 2 3 4
可以是1 2 4
、1 3 4
但不可以2 1 3 4
consecutive
@Testpublic void consecutiveDemo() {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
@Testpublic void answerDemo() {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
@Testpublic void spyDemo() {List list = new LinkedList();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 上的操作不影响 spylist.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
@Testpublic void oneLinerStubsDemo() {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