Mockito库能够Mock对象、验证结果以及打桩(stubbing)。比如在测试时,可以用mockito模拟查询数据库的操作,即将查询数据库的方法拦截,并人工设置其返回值,这样就不用真正去数据库中拿取数据了。此外还可以对某个方法的运行结果进行验证等。
1. 相关包和依赖的导入
在使用mockito之前首先要导入包,建议采用静态导入:import static org.mockito.Mockito.*; ,此外,还要在pom.xml文件中导入Junit相关依赖。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
2. 验证方法是否执行
@Test
public void mockTest() {
List mockedList = mock(List.class);
mockedList.add(1);
verify(mockedList).add(1);
mockedList.clear();
verify(mockedList).clear();
}
如果所验证的方法执行了,则不输出任何内容,反之,如果没有执行则会输出报错信息。
3. 测试桩
@Test
public void mockTest() {
List mockedList = mock(List.class);
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(0));
System.out.println(mockedList.get(1));
}
其输出为 first 和 RuntimeExceptioni类型的异常信息。
基于注解的方式
基于注解的方式可以让代码更加简介,也是最为推荐的。在使用基于注解的mock时,需要先让代码支持mock,有三种方式:
-
在测试类上添加@RunWith(MockitoJUnitRunner.class) 注解 -
在测试类里添加方法,并让其在所有测试方法执行前执行
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
-
在测试类中添加 @Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
以上三种任选一种即可,建议用第一种。
下面给出完整的测试代码:
package cn.ecnu;
import java.util.List;
import cn.ecnu.domain.User;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.MockitoRule;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class MockTest {
@Mock
List mockedList;
@Test
public void mockTest() {
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
System.out.println(mockedList.get(0));
System.out.println(mockedList.get(1));
}
}
其输出和上面的代码一样
参数匹配器
@Test
public void mockTest() {
List mockedList = mock(List.class);
when(mockedList.get(isA(Integer.class))).thenReturn("isA");
System.out.println(mockedList.get(0));
when(mockedList.get(any(Integer.class))).thenReturn("any");
System.out.println(mockedList.get(1));
when(mockedList.get(anyInt())).thenReturn("anyInt");
when(mockedList.get(eq(1))).thenReturn("equal");
System.out.println(mockedList.get(0));
System.out.println("----------");
System.out.println(mockedList.get(1));
}
以上代码的输出为:
isA
any
anyInt
----------
equal
而当将第15行注释后,输出为:
isA
any
anyInt
----------
anyInt
猜测是更准确的参数匹配器会覆盖不准确的,而不是同时起效。
当然自带的参数匹配器还有anyString()、anyList(),、anyCollection()等,不再赘述。
验证方法的调用次数
@Test
public void mockTest() {
List mockedList = mock(List.class);
mockedList.add(1);
mockedList.add(1);
verify(mockedList,times(2)).add(1);
verify(mockedList,atLeast(2)).add(1);
verify(mockedList,atMost(2)).add(1);
verify(mockedList, never()).add(1);
verify(mockedList).add(1);
}
如果验证通过,则不输出任何内容,反之输出对应的错误信息。
无返回值的方法执行时抛异常
@Test
public void mockTest() {
List mockedList = mock(List.class);
doThrow(new RuntimeException()).when(mockedList).clear();
mockedList.clear();
}
验证是否按指定顺序执行
验证单个mock对象的执行顺序
@Test
public void mockTest() {
List mockedList = mock(List.class);
mockedList.add(1);
mockedList.add(2);
mockedList.add(3);
InOrder inOrder = inOrder(mockedList);
inOrder.verify(mockedList).add(1);
inOrder.verify(mockedList).add(2);
inOrder.verify(mockedList).add(3);
}
当按指定顺序执行时不输出任何内容,反之输出错误信息。需要注意的是只要执行序列中出现了指定的这个序列就验证通过。比如,添加顺序为“1 3 2 3”或“1 2 3 4”都是验证通过的。
验证多个mock对象的执行顺序
@Test
public void mockTest() {
List firstMock = mock(List.class);
List secondMock = mock(List.class);
firstMock.add(1);
secondMock.add(2);
InOrder inOrder = inOrder(firstMock,secondMock);
inOrder.verify(firstMock).add(1);
inOrder.verify(secondMock).add(2);
}
使用方法类似,不再赘述。
为连续的调用做测试桩
@Test
public void mockTest() {
List mockedList = mock(List.class);
when(mockedList.get(0))
.thenReturn("firstReturn")
.thenReturn("secondReturn")
.thenThrow(new RuntimeException());
mockedList.add(1);
mockedList.add(2);
System.out.println(mockedList.get(0));
System.out.println(mockedList.get(0));
System.out.println(mockedList.get(1));
System.out.println(mockedList.get(0));
}
上述代码的输出为:
firstReturn
secondReturn
null
java.lang.RuntimeException
......
一种更简洁的版本是把测试桩改成:
when(mockedList.get(0))
.thenReturn("firstReturn", "secondReturn")
.thenThrow(new RuntimeException());
其输出和之前一样。
调用原方法执行
class User{
public int method(){
return 222;
}
}
@Test
public void mockTest(){
User mockedUser = mock(User.class);
when(mockedUser.method()).thenReturn(111);
System.out.println(mockedUser.method());
when(mockedUser.method()).thenCallRealMethod();
System.out.println(mockedUser.method());
}
监控真实对象
spy和mock是相反功能。 spy:如果不对spy对象的methodA打桩,那么调用spy对象的methodA时,会调用真实方法。 mock:如果不对mock对象的methodA打桩,将doNothing,且返回默认值(null,0,false)。
@Test
public void mockTest() {
List spyList = spy(ArrayList.class);
when(spyList.size()).thenReturn(10086);
doReturn(10086).when(spyList).size();
spyList.add(5);
System.out.println(spyList.get(0));
System.out.println(spyList.size());
}
注解版:
@RunWith(MockitoJUnitRunner.class)
public class MockTest {
@Spy
List spyList = new ArrayList();
@Test
public void mockTest() {
when(spyList.size()).thenReturn(10086);
doReturn(10086).when(spyList).size();
spyList.add(5);
System.out.println(spyList.get(0));
System.out.println(spyList.size());
}
}
超时验证
@RunWith(MockitoJUnitRunner.class)
public class MockTest {
@Mock
List mockedList;
@Test
public void mockTest() throws InterruptedException {
mockedList.add(1);
mockedList.add(1);
verify(mockedList,timeout(100)).add(1);
verify(mockedList,timeout(100).times(2)).add(1);
verify(mockedList,timeout(100).atLeast(2)).add(1);
}
}
|