前言
向很多开源社区提交代码是需要做完整的 单元测试 的,其中 mock 目标实例、给 mock 对象打桩 等操作可以极大提高测试代码的效率和可读性
本章节基于 Mockito BDDMockito 的 API DEMO 熟悉一些基本操作,Spring Test 包含对应依赖
Mockito BDDMockito
其中 BDD 代表 behavior driven deployment ,我理解就是让测试代码语义更接近现实行为,比如
given 就是 给定 ... 条件下 ,可以类比为 打桩 (当然也可以不打)when 就是 发生 ... 行为 ,比如 mock 实例的方法调用(当然也可以不发生)then ,就是 则 ... ,打桩 的 mock 实例执行目标方法后的校验
语义化的东西强行解释总感觉生硬
mock
@Test
public void mockDemo() {
List mock = mock(List.class);
mock.add("test");
mock.add("test1");
mock.add("test1");
mock.get(0);
verify(mock).add("test");
then(mock).should().add("test");
verify(mock, times(1)).add("test");
then(mock).should(times(1)).add("test");
verify(mock, atLeast(2)).add("test1");
then(mock).should(atLeast(2)).add("test1");
verify(mock, atMost(2)).add("test1");
then(mock).should(atMost(2)).add("test1");
verify(mock, never()).add("test2");
then(mock).should(never()).add("test2");
verify(mock).add("test2");
verify(mock).get(1);
}
mock 方法创建 mock 对象 ,即一个临时虚拟对象,测试基于该示例进行verify 是 Mockito 下的 API ,类似于断言,比如 verify(mock).add("test") 就是检验该方法是否调用then 是 BDDMockito 的 API ,类比于 verify 更加语义化
stub
@Test
public void stubDemo() {
ArrayList mock = mock(ArrayList.class);
when(mock.get(0)).thenReturn("a");
given(mock.get(0)).willReturn("a");
when(mock.get(1)).thenThrow(new RuntimeException("test"));
doThrow(new RuntimeException("void")).when(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
@Test
public void matcherDemo() {
ArrayList mock = mock(ArrayList.class);
when(mock.get(anyInt())).thenReturn("meta");
given(mock.get(anyInt())).willReturn("meta");
when(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));
verify(mock).get(anyInt());
then(mock).should().get(anyInt());
}
- 可以对参数进行匹配,比如
anyInt() 匹配任意 Integer - 这个针对
Mockito BDDMockito 都一样 - 自然语义下的
then 阶段也可以使用 matcher 来匹配
我指的自然语义就是 given ... when ... then ...
inorder
@Test
public 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);
inOrder.verify(mock2).add(1);
then(mock2).should(inOrder).add(2);
inOrder.verify(mock).add(1);
}
- 这段代码可以测试
mock 实例的方法执行顺序是否符合预期 - 可以多个实例、多个方法结合使用,可看
demo 理解 - 顺序前后符合即可,可以不严格,即
1 2 3 4 可以是 1 2 4 、1 3 4 但不可以 2 1 3 4
consecutive
@Test
public void consecutiveDemo() {
ArrayList mock = mock(ArrayList.class);
when(mock.get(anyInt()))
.thenReturn(0, 1, 2);
System.out.println(mock.get(0));
System.out.println(mock.get(1));
System.out.println(mock.get(2));
System.out.println(mock.get(3));
reset(mock);
System.out.println(mock.get(0));
}
打桩 的结果可以指定多个,会依此返回- 如果没有其他结果,则保持最后一个
reset 可情况 mock 实例在自然语义 given 和 when 阶段下的所有行为
answer
@Test
public void answerDemo() {
ArrayList mock = mock(ArrayList.class);
when(mock.get(anyInt()))
.then(invocation -> {
Object argument = invocation.getArgument(0);
return argument + "r";
});
given(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
@Test
public void spyDemo() {
List list = new LinkedList();
List spy = spy(list);
spy.add(1);
when(spy.size()).thenReturn(10);
given(spy.size()).willReturn(10);
System.out.println(spy.get(0));
System.out.println(spy.size());
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
@Test
public void oneLinerStubsDemo() {
HelloService helloService = when(mock(HelloService.class).hello())
.thenReturn("hello world")
.getMock();
HelloService 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
|