博客主页:JavaProfessional
一个专注于Java的博主,致力于使用通俗易懂的语句讲解内容,期待您的关注!
当前Spring是Java的一大杀器,但是因为所有依赖关系都已经被Spring解决掉了,就导致我们在写完Spring程序后想写单元测试却无从下手。而且现在ORM框架为我们解决了数据库问题,导致我们的程序离开了数据库之后完全不能执行,想写单元测试更是难上加难。
当你模拟任何类得到一个对象时,该对象将获取该类对象的所有操作。
import java.util.List;
import static org.mockito.Mockito.mock;
@Slf4j
public class AppNoticeServiceTest {
@Test
public void baseOperation() {
List mockedList = mock(List.class);
boolean addSuccess = mockedList.add(1);
if(addSuccess) {
// 程序略
log.info("add执行成功");
} else {
// 程序略
log.info("add执行失败");
}
}
}
上述我们模拟了一个List接口。
思考一下,上一个例子当中,我可以把mockedList
的第一个数据打印出来吗?按照我们之前的逻辑当然可以,我不是有一个add
的操作吗?但是仔细想想,我们模拟的可是List
接口啊,都没有指明实现类,难道mockito
已经智能到这种程度了吗?当然不是,其实mockito对象只是具备了类的方法,但是调用方法并不会产生真实的数据,我们必须告诉mockito对象执行某种操作后,给我一个什么数据。比如上述代码中,我们执行add
操作后,给我返回一个true
或false
,好让我控制之后的程序执行路径。
package com.bootdo.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.List;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Slf4j
public class AppNoticeServiceTest {
@Test
public void baseOperation() {
List mockedList = mock(List.class);
when(mockedList.add(1)).thenReturn(Boolean.FALSE);
boolean addSuccess = mockedList.add(1);
if(addSuccess) {
// 程序略
log.info("add执行成功");
} else {
// 程序略
log.info("add执行失败");
}
}
}
此段代码我们去除了add
操作,因为add
操作并不是我们真实的目的,真实目的是我们想要控制程序的执行走的路径,达到全覆盖。
我们如何验证程序按照我们想要的路径争取执行了呢?可以使用verify
。Mockito verify
方法用于检查是否发生了某些行为,我们可以在测试方法代码的末尾使用Mockito
验证方法,以确保调用了指定的方法。
package com.bootdo.app.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import java.util.List;
import static org.mockito.Mockito.*;
@Slf4j
public class AppNoticeServiceTest {
@Test
public void baseOperation() {
List mockedList = mock(List.class);
when(mockedList.get(1)).thenReturn("one");
String content =(String) mockedList.get(1);
if(content.equals("one")) {
// 程序略
mockedList.add("two");
} else {
// 程序略
mockedList.add("three");
}
verify(mockedList).add("two");
}
}
我们成功验证了mockedList
执行了add("two")
方法,证明走了true分支。
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
verify(mockedList, times(3)).add("three times");
// 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");
这个方法很难理解,而且网上的资料大多数都是直译官网,非常难以理解。以下我们通过一个例子来理解一下:
public class Test {
public String method1(){return "method1";}
public String method2(){return "method2";}
}
public class MockTest {
public static void main(String[] args) {
Test test = mock(Test.class);
test.method1();
verifyNoMoreInteractions(test);
}
}
以上调用了test.method1()
但是却没有验证该结果,所以会报错。
public class MockTest {
public static void main(String[] args) {
Test test = mock(Test.class);
test.method1();
verify(test).method1();
verifyNoMoreInteractions(test);
}
}
以上的程序虽然仅仅掉了test的method1方法,没有调用test的method2方法,但是却在调用method1之后,进行了验证了执行次数(默认为1),所以verifyNoMoreInteractions就不会报错了
如果一个方法需要填写参数,但是输入什么参数并不是很重要,重要的是他的返回值,那么你可以使用参数匹配。
when(mockedList.get(anyInt())).thenReturn("element");
你甚至可以模拟空指针和任意类
when(mock.dryRun(isNull())).thenReturn("state");
when(mock.dryRun(any(String.class))).thenReturn("state");
doThrow(new RuntimeException()).when(mockedList).clear();
// 以下程序执行会抛出异常
mockedList.clear();