用来修饰被测试的类:就是对哪个类中的方法进行单元测试的时候,就用该注解修饰这个类。
步骤:
@Rule
public MokitoRule rule = MockitoJUnit.rule();
注意这里的修饰符public,如果没有这个修饰符的话使用mock测试会报错
@Rule
public ExpectedException thrown = ExpectedException.none();
可以选择忽视抛出的异常?(我是这么理解的不知道是否正确)
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>2.0.2-beta</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
1.通过方法创建
/**
* @description: 使用Mockito启动器进行单元测试
* @author: wei·man cui
* @date: 2020/8/13 11:08
*/
@RunWith(MockitoJUnitRunner.class)
public class MockitoByRunnerTest {
@Test
public void testMock() {
//通过方法的模式,Mock一个对象
AccountDao accountDao = Mockito.mock(AccountDao.class);
// 不会报错,但返回null。不会真正执行 accountDao.findAccount()方法,只是模拟该行为。
Account account = accountDao.findAccount("x", "y");
System.out.println(account);
}
}
2.通过注解创建
/**
* @description: Annotation方式进行Mock
* @author: wei·man cui
* @date: 2020/8/13 11:22
*/
public class MockByAnnotationTest {
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Mock
private AccountDao accountDao;
@Test
public void testMock() {
Account account = accountDao.findAccount("x", "y");
System.out.println(account);
}
}
一个简单的例子:
package Mockito;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
import static org.mockito.Mockito.*;
public class MyTest {
@Test
public void myTest() {
/* 创建 Mock 对象 */
List list = mock(List.class);
/* 设置预期,当调用 get(0) 方法时返回 "111" */
when(list.get(0)).thenReturn("111");
Assert.assertEquals("asd", 1, 1);
/* 设置后返回期望的结果 */
System.out.println(list.get(0));
/* 没有设置则返回 null */
System.out.println(list.get(1));
/* 对 Mock 对象设置无效 */
list.add("12");
list.add("123");
/* 返回之前设置的结果 */
System.out.println(list.get(0));
/* 返回 null */
System.out.println(list.get(1));
/* size 大小为 0 */
System.out.println(list.size());
/* 验证操作,验证 get(0) 调用了 2 次 */
verify(list, times(2)).get(0);
/* 验证返回结果 */
String ret = (String)list.get(0);
Assert.assertEquals(ret, "111");
}
设置方法预期返回
通过 when(list.get(0)).thenReturn(“111”); 来设置当调用 list.get(0) 时返回 “111”,该方法就是 Stub,替换我们实际的操作。
验证方法调用
该方法验证 get(0) 方法调用的次数 verify(list, times(2)).get(0);,还可以设置是否调用过,调用时间等等。
验证返回值
Assert.assertEquals(ret, “111”); 方法验证 Mock 对象方法调用后的返回值是否达到预期。
Mock构造一个List对象的时候,使用add是无效的(代表对象的真正的方法),只有使用Spy的时候才能使之生效。
代码如下所示:
@RunWith(MockitoJUnitRunner.class)
public class SpyingTest {
/**
* Spy:用于部分方法的 Mock(模拟)
*/
@Test
public void testSpy() {
List<String> realList = new ArrayList<>();
List<String> list = Mockito.spy(realList);
list.add("Mockito");
list.add("PowerMock");
assertThat(list.get(0), equalTo("Mockito"));
assertThat(list.get(1), equalTo("PowerMock"));
assertThat(list.isEmpty(), equalTo(false));
// Stubbing 语法 模拟 list 的返回内容
Mockito.when(list.isEmpty()).thenReturn(true);
Mockito.when(list.size()).thenReturn(0);
assertThat(list.get(0), equalTo("Mockito"));
assertThat(list.get(1), equalTo("PowerMock"));
assertThat(list.isEmpty(), equalTo(true));
assertThat(list.size(), equalTo(0));
}
}
或者使用注解的方式:
public class SpyingAnnotationTest {
@Spy
private List<String> list = new ArrayList<>();
@Before
public void init() {
MockitoAnnotations.initMocks(SpyingAnnotationTest.class);
}
@Test
public void testSpy() {
list.add("Mockito");
list.add("PowerMock");
assertThat(list.get(0), equalTo("Mockito"));
assertThat(list.get(1), equalTo("PowerMock"));
assertThat(list.isEmpty(), equalTo(false));
// Stubbing 语法 模拟 list 的返回内容
Mockito.when(list.isEmpty()).thenReturn(true);
Mockito.when(list.size()).thenReturn(0);
assertThat(list.get(0), equalTo("Mockito"));
assertThat(list.get(1), equalTo("PowerMock"));
assertThat(list.isEmpty(), equalTo(true));
assertThat(list.size(), equalTo(0));
}
}
下面是Mockito具体使用的一些示例:
mock外部依赖对象,并注入到我们的业务类中,以便在单元测试中进行模拟调用:
@RunWith(MockitoJUnitRunner.class) //让测试运行于Mockito环境
public class LocalServiceImplMockTest {
@InjectMocks //此注解表示这个对象需要被注入mock对象
private LocalServiceImpl localService;
@Mock //此注解会自动创建1个mock对象并注入到@InjectMocks对象中
private RemoteServiceImpl remoteService;
//如果不使用上述注解,可以使用@Before方法来手动进行mock对象的创建和注入,但会几行很多代码
/*
private LocalServiceImpl localService;
private RemoteServiceImpl remoteService;
@Before
public void setUp() throws Exception {
localService = new LocalServiceImpl();
remoteService = Mockito.mock(RemoteServiceImpl.class); //创建Mock对象
Whitebox.setInternalState(localService, "remoteService", remoteService); //注入依赖对象
}
*/
@Test
public void testMock() {
Node target = new Node(1, "target"); //创建一个Node对象作为返回值
Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); //指定当remoteService.getRemoteNode(int)方法传入参数为1时返回target对象
Node result = localService.getRemoteNode(1); //调用我们的业务方法,业务方法内部调用依赖对象方法
assertEquals(target, result); //可以断言我们得到的返回值其实就是target对象
assertEquals(1, result.getNum()); //具体属性和我们指定的返回值相同
assertEquals("target", result.getName()); //具体属性和我们指定的返回值相同
Node result2 = localService.getRemoteNode(2); //未指定参数为2时对应的返回规则
assertNull(result2); //未指定时返回为null
}
}
1.spy外部依赖对象,并注入到我们的业务类中:
@RunWith(MockitoJUnitRunner.class)
public class LocalServiceImplSpyTest {
@InjectMocks
private LocalServiceImpl localService;
@Spy //注意这里使用的是@Spy注解
private RemoteServiceImpl remoteService;
//注意如果自己创建spy对象的话要这么写:
/*
remoteService = new RemoteServiceImpl(); //先创建一个具体实例
remoteService = Mockito.spy(remoteService); //再调用Mockito.spy(T)方法创建spy对象
*/
@Test
public void testSpy() {
Node target = new Node(1, "target"); //创建一个Node对象作为返回值
Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); //指定当remoteService.getRemoteNode(int)方法传入参数为1时返回target对象
Node result = localService.getRemoteNode(1); //调用我们的业务方法,业务方法内部调用依赖对象方法
assertEquals(target, result); //可以断言我们得到的返回值其实就是target对象
assertEquals(1, result.getNum()); //具体属性和我们指定的返回值相同
assertEquals("target", result.getName()); //具体属性和我们指定的返回值相同
Node result2 = localService.getRemoteNode(2); //未指定参数为2时的调用规则,所以会直接调用真实对象,返回remote创建的节点
assertEquals(2, result2.getNum());
assertEquals("Node from remote service", result2.getName()); //remoteService创建Node对象时设置name属性为"Node from remote service"
}
}
1.使用ArgumentMatchers的any系列方法指定多种返回值,有any()、anyInt()、anyString()、anyByte()、anyLong()等等,可以看下ArgumentMatchers类源码中定义的所有方法:
@Test
public void testAny() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target); //静态导入Mockito.when和ArgumentMatchers.anyInt后可以简化代码提升可读性
Node result = localService.getRemoteNode(20); //上面指定了调用remoteService.getRemoteNode(int)时,不管传入什么参数都会返回target对象
assertEquals(target, result); //可以断言我们得到的返回值其实就是target对象
assertEquals(1, result.getNum()); //具体属性和我们指定的返回值相同
assertEquals("target", result.getName()); //具体属性和我们指定的返回值相同
}
指定mock对象多次调用的返回值:
/**
* 指定mock多次调用返回值
*/
@Test
public void testMultipleReturn() {
Node target1 = new Node(1, "target");
Node target2 = new Node(1, "target");
Node target3 = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target1).thenReturn(target2).thenReturn(target3);
//第一次调用返回target1、第二次返回target2、第三次返回target3
Node result1 = localService.getRemoteNode(1); //第1次调用
assertEquals(target1, result1);
Node result2 = localService.getRemoteNode(2); //第2次调用
assertEquals(target2, result2);
Node result3 = localService.getRemoteNode(3); //第3次调用
assertEquals(target3, result3);
}
指定mock对象抛出异常(注意如果方法中未声明会抛出异常,只能指定抛出运行时异常,如果仍指定为抛出受检查异常,运行时会报错误org.mockito.exceptions.base.MockitoException: Checked exception is invalid for this method!):
//RemoteServiceImpl方法:
@Override
public Node getRemoteNode(String name) throws MockException {
if (StringUtils.isEmpty(name)) {
throw new MockException("name不能为空", name);
}
return new Node(name);
}
//LocalServiceImpl方法
@Override
public Node getRemoteNode(String name) throws MockException {
try {
return remoteService.getRemoteNode(name);
} catch (IllegalArgumentException e) {
throw e;
}
}
/**
* 指定mock对象已声明异常抛出的方法抛出受检查异常
*/
@Test
public void testExceptionDeclare() {
try {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode("name")).thenReturn(target).thenThrow(new MockException(
"message", "exception")); //第一次调用正常返回,第二次则抛出一个Exception
Node result1 = localService.getRemoteNode("name");
assertEquals(target, result1); //第一次调用正常返回
Node result2 = localService.getRemoteNode("name"); //第二次调用不会正常返回,会抛出异常
assertEquals(target, result2);
} catch (MockException e) {
assertEquals("exception", e.getName()); //验证是否返回指定异常内容
assertEquals("message", e.getMessage()); //验证是否返回指定异常内容
}
}
/**
* 指定mock对象为声明异常抛出的方法抛出运行时异常
*/
@Test
public void testRuntimeException() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(1)).thenThrow(new RuntimeException("exception")); //指定调用时抛出一个运行时异常
try {
Node result = localService.getRemoteNode(1);
assertEquals(target, result);
} catch (RuntimeException e) {
assertEquals("exception", e.getMessage());
}
}
/**
* 指定mock对象未声明异常抛出的方法抛出受检查异常,以下方法执行会报错
*/
@Test
public void testNotDefineCheckedException() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(1)).thenThrow(new IOException("io exception"));
try {
Node result = localService.getRemoteNode(1);
assertEquals(target, result);
} catch (Exception e) {
assertEquals("io exception", e.getMessage());
}
}
mock void方法抛异常、什么都不做:
//RemoteServiceImpl方法:
@Override
public void doSometing() {
System.out.println("remote service do something!");
}
//LocalServiceImpl方法
@Override
public void remoteDoSomething() {
remoteService.doSometing();
}
//注意void方法没有返回值,所以mock规则写法顺序不一样
doNothing().when(remoteService).doSometing();
doThrow(new RuntimeException("exception")).when(remoteService).doSometing();
校验mock对象的调用情况(除Mockito中的never()、times(int)方法外,还有atLeast(int)、atLeastOne()、atMost(int)等方法):
/**
* 校验mock对象和方法的调用情况
*
*/
public void testVerify() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
verify(remoteService, never()).getRemoteNode(1); //mock方法未调用过
localService.getRemoteNode(1);
Mockito.verify(remoteService, times(1)).getRemoteNode(anyInt()); //目前mock方法调用过1次
localService.getRemoteNode(2);
verify(remoteService, times(2)).getRemoteNode(anyInt()); //目前mock方法调用过2次
verify(remoteService, times(1)).getRemoteNode(2); //目前mock方法参数为2只调用过1次
}
利用ArgumentCaptor捕获方法参数进行mock方法参数校验
/**
* 利用ArgumentCaptor捕获方法参数进行mock方法参数校验
*/
@Test
public void testCaptor() throws Exception {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyString())).thenReturn(target);
localService.getRemoteNode("name1");
localService.getRemoteNode("name2");
verify(remoteService, atLeastOnce()).getRemoteNode(localCaptor.capture()); //设置captor
assertEquals("name2", localCaptor.getValue()); //获取最后一次调用的参数
List<String> list = localCaptor.getAllValues(); //按顺序获取所有传入的参数
assertEquals("name1", list.get(0));
assertEquals("name2", list.get(1));
}
mock对象调用真实方法:
/**
* mock对象调用真实方法
*/
@Test
public void testCallRealMethod() {
when(remoteService.getRemoteNode(anyInt())).thenCallRealMethod(); //设置调用真实方法
Node result = localService.getRemoteNode(1);
assertEquals(1, result.getNum());
assertEquals("Node from remote service", result.getName());
}
重置mock对象:
//重置mock,清除所有的调用记录和返回规则
Mockito.reset(remoteService);
校验mock对象0调用和未被验证的调用
/**
* 校验mock对象0调用和未被验证的调用
*/
@Test(expected = NoInteractionsWanted.class)
public void testInteraction() {
verifyZeroInteractions(remoteService); //目前还未被调用过,执行不报错
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
localService.getRemoteNode(1);
localService.getRemoteNode(2);
verify(remoteService, times(2)).getRemoteNode(anyInt());
// 参数1和2的两次调用都会被上面的anyInt()校验到,所以没有未被校验的调用了
verifyNoMoreInteractions(remoteService);
reset(remoteService);
localService.getRemoteNode(1);
localService.getRemoteNode(2);
verify(remoteService, times(1)).getRemoteNode(1);
// 参数2的调用不会被上面的校验到,所以执行会抛异常
verifyNoMoreInteractions(remoteService);
}
设置 Mock 对象期望和返回值
/* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 */
when(mock.someMethod()).thenReturn(value1).thenReturn(value2);
when(mock.someMethod()).thenReturn(value1, value2);
/* 也可以设置两次 */
when(mock.someMethod()).thenReturn(value1);
when(mock.someMethod()).thenReturn(value2);
另外一种写法 doReturn()
/* 表示第一次调用 someMethod() 返回 value1 第二次调用返回 value2 */
doReturn(value1).doReturn(value2).when(mock).someMethod();
/* 若返回 void,则设置为 doNothing() */
doNothing().when(mock).someMethod();
对方法设定返回异常
/* 当调用 someMethod() 方法时会抛出异常 */
when(mock.someMethod()).thenThrow(new RuntimeException());
/* 对 void 方法设定 */
doThrow(new RuntimeException()).when(mock).someMethod();
参数匹配器
我们不一定要固定 Stub 调用时的参数,如 get(0)。可以通过参数匹配器来调用。
when(list.get(anyInt())).thenReturn("hello");
不同的调用次数返回不同的值
@Test
public void Test01(){
//这种方式,第一次调用返回1,第二次调用返回2,4次以后,都一直返回4.
Mockito.when(list.size()).thenReturn(1,2,3,4);
asertThat(list.size(),equalTo(1));
asertThat(list.size(),equalTo(2));
asertThat(list.size(),equalTo(3));
asertThat(list.size(),equalTo(4));
//与上面等价的一种方式
Mockito.when(list.size()).thenReturn(1).thenReturn(2).thenReturn(3).thenReturn(4);
}
package Mockito;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.testng.annotations.Test;
import java.util.List;
import static org.mockito.Mockito.*;
/**
* Created by andy.wwh on 2016/7/18.
*/
public class Behavior {
@Test
public void behaviorCheck() {
List mock1 = mock(List.class);
List mock2 = mock(List.class);
/* 设置预期 */
when(mock1.get(0)).thenReturn("hello world");
when(mock1.get(1)).thenReturn("hello world");
when(mock2.get(0)).thenReturn("hello world");
mock1.get(0);
/* 验证方法调用一次 */
verify(mock1).get(0);
mock1.get(0);
/* 验证方法调用两次 */
verify(mock1, times(2)).get(0);
/* 验证方法从未被调用过 */
verify(mock2, never()).get(0);
/* 验证方法 100 毫秒内调用两次 */
verify(mock1, timeout(100).times(2)).get(anyInt());
/* 设置方法调用顺序 */
InOrder inOrder = inOrder(mock1, mock2);
inOrder.verify(mock1, times(2)).get(0);
inOrder.verify(mock2, never()).get(1);
/* 查询是否存在被调用,但未被 verify 验证的方法 */
verifyNoMoreInteractions(mock1, mock2);
/* 验证 Mock 对象是否没有交发生 */
verifyZeroInteractions(mock1, mock2);
/* 参数捕获器 */
ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor.forClass(Integer.class);
verify(mock1, times(2)).get(argumentCaptor.capture());
System.out.println("argument:" + argumentCaptor.getValue());
}
}
verify(mock1, timeout(100).times(2)).get(anyInt());
除了代码中的方法,Mockito 还提供了
超时验证
通过 timeout 我们可以进行验证程序执行时间是否符合规则。
方法调用顺序
Inorder 可以验证方法调用的顺序
verifyNoMoreInteractions 和 verifyZeroInteractions
verifyNoMoreInteractions:查询是否存在被调用,但未被 verify 验证的方法
verifyZeroInteractions:verifyZeroInteractions
ArgumentCaptor 参数捕获器
可在验证时对方法的参数进行捕获,最后验证捕获的参数值。如果方法有多个参数都要捕获验证,那就需要创建多个ArgumentCaptor对象处理。
Mock 操作的全是虚拟对象。即使我们设置了 when(list.get(0)).thenReturn(1),我们调用如 size() 方法返回的还是 0。Mockito 还给我们提供了一种对真实对象操作的方法——Spy
做一个简单的比较:
package Mockito;
import org.testng.annotations.Test;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Created by andy.wwh on 2016/7/18.
*/
public class MockObject {
@Test
public void MockTest() {
List list = mock(List.class);
when(list.get(0)).thenReturn("hello world");
System.out.println(list.get(0));
System.out.println(list.size());
}
}
package Mockito;
import org.testng.annotations.Test;
import java.util.LinkedList;
import java.util.List;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
/**
* Created by andy.wwh on 2016/7/18.
*/
public class MockObject {
@Test
public void MockTest() {
/* 创建真实对象 */
List list = new LinkedList();
List spy = spy(list);
spy.add("hello");
when(spy.get(0)).thenReturn("hello world");
System.out.println(spy.get(0));
}
}
看个官网的例子:
@Test
public void spyTest() {
List list = new LinkedList();
List spy = spy(list);
// optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);
// using the spy calls real methods
spy.add("one");
spy.add("two");
// prints "one" - the first element of a list
System.out.println(spy.get(0));
// size() method was stubbed - 100 is printed
System.out.println(spy.size());
// optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");