目录
1 Mockito
mockito是Java中用于单元测试的模拟框架,Mockito库支持模拟创建、验证和存根。
通常和Junit框架组合完成对项目的单元测试。
2 各种模拟使用
2.1 mock接口并验证
测试demo-1:
//1--mock一些接口, 使用verify验证某些行为是否发生仅过一次@TestvoidmockListTest(){List listMock =mock(List.class);
listMock.add("one");
listMock.get(1);
listMock.clear();verify(listMock).add("one");verify(listMock).clear();verify(listMock).get(1);}
mock
接口,并可
verify
行为是否发生过和已经发生次数。
2.2 mock具体类,并进行stub
测试demo-2:
//2--mock一些具体的类,并进行stub@TestvoidmockConcreteList(){LinkedList mockLinkedList =mock(LinkedList.class);when(mockLinkedList.get(0)).thenReturn("first");when(mockLinkedList.get(1)).thenThrow(newRuntimeException());System.out.println(mockLinkedList.get(0));// System.out.println(mockLinkedList.get(1));System.out.println(mockLinkedList.get(999));}
mock
具体类,
stub
具体的行为返回值。
2.3 参数匹配器-Argument matchers
测试demo-3:
@TestvoidmockArgumentMatcherTest(){LinkedList mockLinkedList =mock(LinkedList.class);when(mockLinkedList.get(anyInt())).thenReturn("element");System.out.println(mockLinkedList.get(Integer.MAX_VALUE));//可以传入lambda表达式when(mockLinkedList.contains(argThat((str)->{String strNew =(String) str;return strNew.length()>5;}))).thenReturn(true);System.out.println(mockLinkedList.contains("cece"));System.out.println(mockLinkedList.contains("cecece"));verify(mockLinkedList).get(anyInt());}
运行结果:
可以使用内建的
anyInt
……系列或者自定义
argThat
,来完成stub需求,支持lambda表达式。
2.4 验证实际调用的次数
测试demo-4:
@TestvoidverifyExactNumberTest(){LinkedList mockLinkedList =mock(LinkedList.class);
mockLinkedList.add("one");
mockLinkedList.add("two");
mockLinkedList.add("two");
mockLinkedList.add("three");
mockLinkedList.add("three");
mockLinkedList.add("three");//默认一次verify(mockLinkedList).add("one");verify(mockLinkedList,times(1)).add("one");//验证两次、三次verify(mockLinkedList,times(2)).add("two");verify(mockLinkedList,times(3)).add("three");//验证从未添加的元素verify(mockLinkedList,never()).add("never add");//验证atLeast、atMostverify(mockLinkedList,atLeast(1)).add("three");verify(mockLinkedList,atMost(1)).add("one");}
可以使用支持的
times、never、atLeast和atMost
进行调用次数验证。
2.5 stub 空返回值方法
测试demo-5:
@TestvoidstubVoidTest(){LinkedList mockLinkedList =mock(LinkedList.class);doThrow(newRuntimeException()).when(mockLinkedList).clear();//调用clear方法,返回异常
mockLinkedList.clear();}
执行结果:
mock
返回值为
void
方法。
2.6 按顺序验证行为
测试demo-6:
@TestvoidverifyInOrderTest(){List singleList =mock(List.class);
singleList.add("was add first");
singleList.add("was add second");//单一mock验证顺序InOrder singleInOrder =inOrder(singleList);
singleInOrder.verify(singleList).add("was add first");
singleInOrder.verify(singleList).add("was add second");List firstMock =mock(List.class);List secondMock =mock(List.class);
firstMock.add("one");
firstMock.add("two");
secondMock.add("one");
secondMock.add("two");InOrder order =inOrder(firstMock, secondMock);//多个mock验证添加顺序
order.verify(firstMock).add("two");
order.verify(secondMock).add("one");
order.verify(secondMock).add("two");}
通过
InOrder
进行行为顺序验证,支持多个
mock
对象的顺序验证。
2.7 冗余的mock对象验证
测试demo-7:
@TestvoidredundantInvocationTest(){List mockList =mock(List.class);
mockList.add("one");
mockList.add("two");//这个没有验证
mockList.add("three");verify(mockList).add("two");verify(mockList).add("one");verifyNoMoreInteractions(mockList);}
执行结果:
verifyNoMoreInteractions
支持验证多余的交互行为(交互了但没有
verify
)。
2.8 迭代器风格的stub
测试demo-8:
@TestvoidstubIteratorTest(){List mockList =mock(List.class);when(mockList.get(1)).thenReturn("foo","test");//按顺序返回System.out.println(mockList.get(1));System.out.println(mockList.get(1));//之后都是最后一个迭代test获胜System.out.println(mockList.get(1));}
执行结果:
可以返回多个值,每次调用都会向后迭代,超过长度会重复最后一个
stub
的结果。
2.9 支持回调的stub
测试demo-9:
@TestvoidstubWithCallbackTest(){List<String> mockList =mock(List.class);//使用thenAnswer传入回调when(mockList.get(anyInt())).thenAnswer(invocation ->{Object[] arguments = invocation.getArguments();Object mock = invocation.getMock();return"called with arguments: "+Arrays.toString(arguments);});System.out.println(mockList.get(111));}
运行结果:
支持通过
thenAnswer
传入lambda表达式,自定义回调函数处理返回结果。
2. 10 spy真实对象
测试demo-10:
@TestvoidspyRealObjectTest(){LinkedList<String> linkedList =newLinkedList<>();LinkedList<String> spy =spy(linkedList);
spy.add("one");
spy.add("two");System.out.println(spy.get(0));System.out.println(spy.get(1));//进行stubwhen(spy.size()).thenReturn(100);System.out.println(spy.size());verify(spy).add("one");List list =newLinkedList();List spy1 =spy(list);}
运行结果:
当
spy
真实对象,将会调用对象的真实方法,同时可以支持
stub
对象行为,但是有局限性,比如下面情况:
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)when(spy1.get(0)).thenReturn("foo");//You have to use doReturn() for stubbingdoReturn("foo").when(spy).get(0);
运行结果:
因为使用
when,spy.get(0)
将被调用,而此时还没有元素,会抛出异常。而
doReturn
没有调用方法,只是设置了返回值。
2-11 部分真实mock
测试demo-11:
@TestvoidrealPartMockTest(){CalculatorMapper mock =mock(CalculatorMapper.class);//调用selectFirst调用真实方法when(mock.selectFirst(anyInt())).thenCallRealMethod();Assertions.assertEquals(101, mock.selectFirst(1));}
thenCallRealMethod
支持调用真实对象方法。
2-12 重置mock
测试demo-12:
@TestvoidresetMockTest(){List mock =mock(List.class);when(mock.size()).thenReturn(10);
mock.add(1);//reset所有发生的调用和行为reset(mock);verifyNoMoreInteractions(mock);}
一般我们测试都是针对单独的一个功能,没有必要进行重置。
2-13 @Mock和@InjectMocks
测试demo-13:
@ExtendWith(MockitoExtension.class)publicclassCalculatorManagerTest{//注入mock,会对注入的属性根据测试类中定义的mock类进行注入。@InjectMocksprivateCalculatorService calculatorService;//mock其它类,需要对方法返回值进行mock设置@MockprivateCalculatorMapper calculatorMapper;@BeforeEachvoidsetUp(){}@TestpublicvoidcalculatorManagerTest(){when(calculatorMapper.selectFirst(anyInt())).thenReturn(100);when(calculatorMapper.selectSecond(anyInt())).thenReturn(100);Assertions.assertEquals(200, calculatorService.add(1,1));}}
可以使用注解
@Mock
更简单的
mock
一个对象,如果对象中有注入对象,可以使用
@InjectMocks
进行
mock
对象注入。
2-14 行为驱动开发模式BDD
BDD
一般定义为下:
- 给定
given
:场景开头的初始上下文,一个或多个子句. - 时间
when
:触发场景的事件. - 然后
then
:在一个或多个条款中的预期结果。
测试demo-14:
@TestvoidbddMockTest(){List mockList =mock(List.class);//given,列表获取常见given(mockList.get(anyInt())).willReturn(100,200,300);//when,第一次获取
mockList.get(0);//then,判断获取第二次结果Assertions.assertEquals(mockList.get(0),200);//verify行为的bdd模式then(mockList).should(times(2)).get(0);}
mockito工作组建议使用的编写测试模式。
库中已经封装了对于的
BDD
调用方式。
参考文献
[1] Mockito文档.
版权归原作者 LamaxiyaFc 所有, 如有侵权,请联系我们删除。