mockito是Java中用于单元测试的模拟框架,Mockito库支持模拟创建、验证和存根。
通常和Junit框架组合完成对项目的单元测试。
测试demo-1:
//1--mock一些接口, 使用verify验证某些行为是否发生仅过一次
@Test
void mockListTest() {
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
行为是否发生过和已经发生次数。
测试demo-2:
//2--mock一些具体的类,并进行stub
@Test
void mockConcreteList() {
LinkedList mockLinkedList = mock(LinkedList.class);
when(mockLinkedList.get(0)).thenReturn("first");
when(mockLinkedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockLinkedList.get(0));
// System.out.println(mockLinkedList.get(1));
System.out.println(mockLinkedList.get(999));
}
mock
具体类,stub
具体的行为返回值。
测试demo-3:
@Test
void mockArgumentMatcherTest() {
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表达式。
测试demo-4:
@Test
void verifyExactNumberTest() {
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、atMost
verify(mockLinkedList, atLeast(1)).add("three");
verify(mockLinkedList, atMost(1)).add("one");
}
可以使用支持的times、never、atLeast和atMost
进行调用次数验证。
测试demo-5:
@Test
void stubVoidTest() {
LinkedList mockLinkedList = mock(LinkedList.class);
doThrow(new RuntimeException()).when(mockLinkedList).clear();
//调用clear方法,返回异常
mockLinkedList.clear();
}
mock
返回值为void
方法。
测试demo-6:
@Test
void verifyInOrderTest() {
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
对象的顺序验证。
测试demo-7:
@Test
void redundantInvocationTest() {
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
)。
测试demo-8:
@Test
void stubIteratorTest() {
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
的结果。
测试demo-9:
@Test
void stubWithCallbackTest() {
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表达式,自定义回调函数处理返回结果。
测试demo-10:
@Test
void spyRealObjectTest() {
LinkedList<String> linkedList = new LinkedList<>();
LinkedList<String> spy = spy(linkedList);
spy.add("one");
spy.add("two");
System.out.println(spy.get(0));
System.out.println(spy.get(1));
//进行stub
when(spy.size()).thenReturn(100);
System.out.println(spy.size());
verify(spy).add("one");
List list = new LinkedList();
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 stubbing
doReturn("foo").when(spy).get(0);
运行结果:
因为使用when,spy.get(0)
将被调用,而此时还没有元素,会抛出异常。而doReturn
没有调用方法,只是设置了返回值。
测试demo-11:
@Test
void realPartMockTest() {
CalculatorMapper mock = mock(CalculatorMapper.class);
//调用selectFirst调用真实方法
when(mock.selectFirst(anyInt())).thenCallRealMethod();
Assertions.assertEquals(101, mock.selectFirst(1));
}
thenCallRealMethod
支持调用真实对象方法。
测试demo-12:
@Test
void resetMockTest() {
List mock = mock(List.class);
when(mock.size()).thenReturn(10);
mock.add(1);
//reset所有发生的调用和行为
reset(mock);
verifyNoMoreInteractions(mock);
}
一般我们测试都是针对单独的一个功能,没有必要进行重置。
测试demo-13:
@ExtendWith(MockitoExtension.class)
public class CalculatorManagerTest {
//注入mock,会对注入的属性根据测试类中定义的mock类进行注入。
@InjectMocks
private CalculatorService calculatorService;
//mock其它类,需要对方法返回值进行mock设置
@Mock private CalculatorMapper calculatorMapper;
@BeforeEach
void setUp() {
}
@Test
public void calculatorManagerTest() {
when(calculatorMapper.selectFirst(anyInt())).thenReturn(100);
when(calculatorMapper.selectSecond(anyInt())).thenReturn(100);
Assertions.assertEquals(200, calculatorService.add(1, 1));
}
}
可以使用注解@Mock
更简单的mock
一个对象,如果对象中有注入对象,可以使用@InjectMocks
进行mock
对象注入。
BDD
一般定义为下:
given
:场景开头的初始上下文,一个或多个子句.when
:触发场景的事件.then
:在一个或多个条款中的预期结果。测试demo-14:
@Test
void bddMockTest() {
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文档.