Mockito
1. Mockito
单元测试开发中,我们经常会遇到测试的类有很多依赖的类、对象、资源,从而形成巨大的依赖树,mock可以模拟外部依赖,适应单元测试。
比如我们在开发中很容易出现这种情况:
(a)依赖(b)依赖(c)依赖(d)
这种情况下单元测试就变得极其复杂,而且如果需要测试数据库或网络请求返回值的情况,就更加复杂了。我们总不至于去修改真实数据库,去修改真实的网络情况吧。
但是如果我们引入Mockito情况就会变得简单许多,我们可以mock虚拟的依赖,并可以轻松的修改返回值甚至抛出异常:
(a)依赖(mock b)
1.1 Mockito 的简单用法
Mockito是一个功能非常丰富的框架,下面介绍几个典型和常用的方法,详细的使用可以到官网上查询。
我们在非Spring Boot项目中使用Mockito需要手动引入依赖,Spring Boot项目中Mocktio的使用会在下面介绍。
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
1.1.1 mock对象
// 模拟LinkedList 的一个对象
LinkedList mockedList = mock(LinkedList.class);
// 此时调用get方法,会返回null,因为还没有对方法调用的返回值做模拟
System.out.println(mockedList.get(0));
1.1.2 方法调用的返回值
// 模拟获取第一个元素时,返回字符串first。 给特定的方法调用返回固定值在官方说法中称为stub。
when(mockedList.get(0)).thenReturn("first");
// 此时打印输出first
System.out.println(mockedList.get(0));
1.1.3 方法调用抛出异常
// 模拟获取第二个元素时,抛出RuntimeException
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 此时将会抛出RuntimeException
System.out.println(mockedList.get(1));
如果一个方法没有返回值类型,那么可以使用此方法模拟异常抛出
doThrow(new RuntimeException("clear exception")).when(mockedList).clear();
mockedList.clear();
1.1.4 调用方法时的参数匹配
// anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element
when(mockedList.get(anyInt())).thenReturn("element");
// 此时打印是element
System.out.println(mockedList.get(999));
1.1.5 验证方法调用次数
// 调用add一次
mockedList.add("once");
// 下面两个写法验证效果一样,均验证add方法是否被调用了一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
1.1.6 校验行为
校验方法调用
// mock creation
List mockedList = mock(List.class);
// using mock object
mockedList.add("one");
mockedList.clear();
// verification
verify(mockedList).add("one");
verify(mockedList).clear();
校验方法调用顺序
// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// Oh, and A + B can be mixed together at will
校验方法是否从未调用
//using mocks - only mockOne is interacted
mockOne.add("one");
//ordinary verification
verify(mockOne).add("one");
//verify that method was never called on a mock
verify(mockOne, never()).add("two");
//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);
1.2 Mockito 在 Spring Boot 中的使用
上面我们提到了非 Spring Boot 需要手动引入 Mocktio 的依赖,但在 Spring Boot 项目中,Spring Boot Test 默认依赖 Mocktio ,所以只要依赖了 Spring Boot Test 就不再需要我们手动引入依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
一般情况下,单元测试不需要 Spring 的启动。此时我们按下面的写法:
import static org.mockito.Mockito.*;
public class MyServiceTest {
/**被单元测试的类*/
@InjectMocks
private MyServiceImpl myServiceImpl;
/**被单元测试的类中所有的依赖*/
@Mock
private MyMapper myMapper;
/**在单元测试之前,执行Mockito的注解扫描,并注入依赖*/
@Before
public void setUp() { MockitoAnnotations.initMocks(this); }
@Test
public void myServiceMethod() {
myServiceImpl.myServiceMethod();
}
}或者通过注解 RunWith 的方式:
import static org.mockito.Mockito.*;
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
/**被单元测试的类*/
@InjectMocks
private MyServiceImpl myServiceImpl;
/**被单元测试的类中所有的依赖*/
@Mock
private MyMapper myMapper;
@Test
public void myServiceMethod() {
myServiceImpl.myServiceMethod();
}
}
但有些情况下,我们可能需要 Spring 启动着。可以按下面这样写:
import static org.mockito.Mockito.*;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {
/**被单元测试的类*/
@InjectMocks
private MyServiceImpl myServiceImpl;
/**被单元测试的类中所有的依赖*/
@Mock
private MyMapper myMapper;
@Test
public void myServiceMethod() {
myServiceImpl.myServiceMethod();
}
}