powermockito教程_Mockito与PowerMock的使用基础教程
一、Mockito與PowerMock簡(jiǎn)述
Mockito與PowerMock都是Java流行的一種Mock框架,使用Mock技術(shù)能讓我們隔離外部依賴以便對(duì)我們自己的業(yè)務(wù)邏輯代碼進(jìn)行單元測(cè)試,在編寫(xiě)單元測(cè)試時(shí),不需要再進(jìn)行繁瑣的初始化工作,在需要調(diào)用某一個(gè)接口時(shí),直接模擬一個(gè)假方法,并任意指定方法的返回值。
Mockito的工作原理是通過(guò)創(chuàng)建依賴對(duì)象的proxy,所有的調(diào)用先經(jīng)過(guò)proxy對(duì)象,proxy對(duì)象攔截了所有的請(qǐng)求再根據(jù)預(yù)設(shè)的返回值進(jìn)行處理。PowerMock則在Mockito原有的基礎(chǔ)上做了擴(kuò)展,通過(guò)修改類字節(jié)碼并使用自定義ClassLoader加載運(yùn)行的方式來(lái)實(shí)現(xiàn)mock靜態(tài)方法、final方法、private方法、系統(tǒng)類的功能。
從兩者的項(xiàng)目結(jié)構(gòu)中就可以看出,PowerMock直接依賴于Mockito,所以如果項(xiàng)目中已經(jīng)導(dǎo)入了PowerMock包就不需要再單獨(dú)導(dǎo)入Mockito包,如果兩者同時(shí)導(dǎo)入還要小心PowerMock和Mockito不同版本之間的兼容問(wèn)題:
Mockito包依賴:
org.mockito
mockito-core
2.23.0
test
PowerMock包依賴:
org.powermock
powermock-module-junit4
2.0.0-RC.3
test
org.powermock
powermock-api-mockito2
2.0.0-RC.3
test
二、Mockito的使用
Mockito一般通過(guò)創(chuàng)建mock或spy對(duì)象,并制定具體返回規(guī)則來(lái)實(shí)現(xiàn)模擬的功能,在調(diào)用完成后還可以進(jìn)行方法調(diào)用驗(yàn)證以檢驗(yàn)程序邏輯是否正確。mock和spy對(duì)象的區(qū)別是mock對(duì)象對(duì)于未指定處理規(guī)則的調(diào)用會(huì)按方法返回值類型返回該類型的默認(rèn)值(如int、long則返回0,boolean則返回false,對(duì)象則返回null,void則什么都不做),而spy對(duì)象在未指定處理規(guī)則時(shí)則會(huì)直接調(diào)用真實(shí)方法。
以下3個(gè)類是我們的項(xiàng)目中需要用到的一些業(yè)務(wù)類:
//實(shí)體類
public class Node {
private int num;
private String name;
//以下忽略所有構(gòu)造方法和get、set方法
}
//本地負(fù)責(zé)實(shí)現(xiàn)具體業(yè)務(wù)的業(yè)務(wù)類
public class LocalServiceImpl implements ILocalService {
//外部依賴
@Autowired
private IRemoteService remoteService;
//具體業(yè)務(wù)處理方法
@Override
public Node getRemoteNode(int num) {
return remoteService.getRemoteNode(num);
}
//以下忽略其他業(yè)務(wù)調(diào)用方法,在后面例子中補(bǔ)充
}
//外部依賴業(yè)務(wù)類,由其他人實(shí)現(xiàn),可能我們的業(yè)務(wù)類寫(xiě)好了別人還沒(méi)寫(xiě)好
public class RemoteServiceImpl implements IRemoteService {
//外部類提供的一些業(yè)務(wù)方法
@Override
public Node getRemoteNode(int num) {
return new Node(num, "Node from remote service");
}
//其他業(yè)務(wù)方法在后面例子中補(bǔ)充
}
下面是Mockito具體使用的一些示例:
mock外部依賴對(duì)象,并注入到我們的業(yè)務(wù)類中,以便在單元測(cè)試中進(jìn)行模擬調(diào)用:
@RunWith(MockitoJUnitRunner.class) //讓測(cè)試運(yùn)行于Mockito環(huán)境
public class LocalServiceImplMockTest {
@InjectMocks //此注解表示這個(gè)對(duì)象需要被注入mock對(duì)象
private LocalServiceImpl localService;
@Mock //此注解會(huì)自動(dòng)創(chuàng)建1個(gè)mock對(duì)象并注入到@InjectMocks對(duì)象中
private RemoteServiceImpl remoteService;
//如果不使用上述注解,可以使用@Before方法來(lái)手動(dòng)進(jìn)行mock對(duì)象的創(chuàng)建和注入,但會(huì)幾行很多代碼
/*
private LocalServiceImpl localService;
private RemoteServiceImpl remoteService;
@Before
public void setUp() throws Exception {
localService = new LocalServiceImpl();
remoteService = Mockito.mock(RemoteServiceImpl.class); //創(chuàng)建Mock對(duì)象
Whitebox.setInternalState(localService, "remoteService", remoteService); //注入依賴對(duì)象
}
*/
@Test
public void testMock() {
Node target = new Node(1, "target"); //創(chuàng)建一個(gè)Node對(duì)象作為返回值
Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); //指定當(dāng)remoteService.getRemoteNode(int)方法傳入?yún)?shù)為1時(shí)返回target對(duì)象
Node result = localService.getRemoteNode(1); //調(diào)用我們的業(yè)務(wù)方法,業(yè)務(wù)方法內(nèi)部調(diào)用依賴對(duì)象方法
assertEquals(target, result); //可以斷言我們得到的返回值其實(shí)就是target對(duì)象
assertEquals(1, result.getNum()); //具體屬性和我們指定的返回值相同
assertEquals("target", result.getName()); //具體屬性和我們指定的返回值相同
Node result2 = localService.getRemoteNode(2); //未指定參數(shù)為2時(shí)對(duì)應(yīng)的返回規(guī)則
assertNull(result2); //未指定時(shí)返回為null
}
}
spy外部依賴對(duì)象,并注入到我們的業(yè)務(wù)類中:
@RunWith(MockitoJUnitRunner.class)
public class LocalServiceImplSpyTest {
@InjectMocks
private LocalServiceImpl localService;
@Spy //注意這里使用的是@Spy注解
private RemoteServiceImpl remoteService;
//注意如果自己創(chuàng)建spy對(duì)象的話要這么寫(xiě):
/*
remoteService = new RemoteServiceImpl(); //先創(chuàng)建一個(gè)具體實(shí)例
remoteService = Mockito.spy(remoteService); //再調(diào)用Mockito.spy(T)方法創(chuàng)建spy對(duì)象
*/
@Test
public void testSpy() {
Node target = new Node(1, "target"); //創(chuàng)建一個(gè)Node對(duì)象作為返回值
Mockito.when(remoteService.getRemoteNode(1)).thenReturn(target); //指定當(dāng)remoteService.getRemoteNode(int)方法傳入?yún)?shù)為1時(shí)返回target對(duì)象
Node result = localService.getRemoteNode(1); //調(diào)用我們的業(yè)務(wù)方法,業(yè)務(wù)方法內(nèi)部調(diào)用依賴對(duì)象方法
assertEquals(target, result); //可以斷言我們得到的返回值其實(shí)就是target對(duì)象
assertEquals(1, result.getNum()); //具體屬性和我們指定的返回值相同
assertEquals("target", result.getName()); //具體屬性和我們指定的返回值相同
Node result2 = localService.getRemoteNode(2); //未指定參數(shù)為2時(shí)的調(diào)用規(guī)則,所以會(huì)直接調(diào)用真實(shí)對(duì)象,返回remote創(chuàng)建的節(jié)點(diǎn)
assertEquals(2, result2.getNum());
assertEquals("Node from remote service", result2.getName()); //remoteService創(chuàng)建Node對(duì)象時(shí)設(shè)置name屬性為"Node from remote service"
}
}
使用ArgumentMatchers的any系列方法指定多種返回值,有any()、anyInt()、anyString()、anyByte()、anyLong()等等,可以看下ArgumentMatchers類源碼中定義的所有方法:
@Test
public void testAny() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target); //靜態(tài)導(dǎo)入Mockito.when和ArgumentMatchers.anyInt后可以簡(jiǎn)化代碼提升可讀性
Node result = localService.getRemoteNode(20); //上面指定了調(diào)用remoteService.getRemoteNode(int)時(shí),不管傳入什么參數(shù)都會(huì)返回target對(duì)象
assertEquals(target, result); //可以斷言我們得到的返回值其實(shí)就是target對(duì)象
assertEquals(1, result.getNum()); //具體屬性和我們指定的返回值相同
assertEquals("target", result.getName()); //具體屬性和我們指定的返回值相同
}
指定mock對(duì)象多次調(diào)用的返回值:
/**
* 指定mock多次調(diào)用返回值
*/
@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);
//第一次調(diào)用返回target1、第二次返回target2、第三次返回target3
Node result1 = localService.getRemoteNode(1); //第1次調(diào)用
assertEquals(target1, result1);
Node result2 = localService.getRemoteNode(2); //第2次調(diào)用
assertEquals(target2, result2);
Node result3 = localService.getRemoteNode(3); //第3次調(diào)用
assertEquals(target3, result3);
}
指定mock對(duì)象拋出異常(注意如果方法中未聲明會(huì)拋出異常,只能指定拋出運(yùn)行時(shí)異常,如果仍指定為拋出受檢查異常,運(yùn)行時(shí)會(huì)報(bào)錯(cuò)誤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對(duì)象已聲明異常拋出的方法拋出受檢查異常
*/
@Test
public void testExceptionDeclare() {
try {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode("name")).thenReturn(target).thenThrow(new MockException(
"message", "exception")); //第一次調(diào)用正常返回,第二次則拋出一個(gè)Exception
Node result1 = localService.getRemoteNode("name");
assertEquals(target, result1); //第一次調(diào)用正常返回
Node result2 = localService.getRemoteNode("name"); //第二次調(diào)用不會(huì)正常返回,會(huì)拋出異常
assertEquals(target, result2);
} catch (MockException e) {
assertEquals("exception", e.getName()); //驗(yàn)證是否返回指定異常內(nèi)容
assertEquals("message", e.getMessage()); //驗(yàn)證是否返回指定異常內(nèi)容
}
}
/**
* 指定mock對(duì)象為聲明異常拋出的方法拋出運(yùn)行時(shí)異常
*/
@Test
public void testRuntimeException() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(1)).thenThrow(new RuntimeException("exception")); //指定調(diào)用時(shí)拋出一個(gè)運(yùn)行時(shí)異常
try {
Node result = localService.getRemoteNode(1);
assertEquals(target, result);
} catch (RuntimeException e) {
assertEquals("exception", e.getMessage());
}
}
/**
* 指定mock對(duì)象未聲明異常拋出的方法拋出受檢查異常,以下方法執(zhí)行會(huì)報(bào)錯(cuò)
*/
@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方法沒(méi)有返回值,所以mock規(guī)則寫(xiě)法順序不一樣
doNothing().when(remoteService).doSometing();
doThrow(new RuntimeException("exception")).when(remoteService).doSometing();
校驗(yàn)mock對(duì)象的調(diào)用情況(除Mockito中的never()、times(int)方法外,還有atLeast(int)、atLeastOne()、atMost(int)等方法):
/**
* 校驗(yàn)mock對(duì)象和方法的調(diào)用情況
*
*/
public void testVerify() {
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
verify(remoteService, never()).getRemoteNode(1); //mock方法未調(diào)用過(guò)
localService.getRemoteNode(1);
Mockito.verify(remoteService, times(1)).getRemoteNode(anyInt()); //目前mock方法調(diào)用過(guò)1次
localService.getRemoteNode(2);
verify(remoteService, times(2)).getRemoteNode(anyInt()); //目前mock方法調(diào)用過(guò)2次
verify(remoteService, times(1)).getRemoteNode(2); //目前mock方法參數(shù)為2只調(diào)用過(guò)1次
}
利用ArgumentCaptor捕獲方法參數(shù)進(jìn)行mock方法參數(shù)校驗(yàn)
/**
* 利用ArgumentCaptor捕獲方法參數(shù)進(jìn)行mock方法參數(shù)校驗(yàn)
*/
@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()); //設(shè)置captor
assertEquals("name2", localCaptor.getValue()); //獲取最后一次調(diào)用的參數(shù)
List list = localCaptor.getAllValues(); //按順序獲取所有傳入的參數(shù)
assertEquals("name1", list.get(0));
assertEquals("name2", list.get(1));
}
mock對(duì)象調(diào)用真實(shí)方法:
/**
* mock對(duì)象調(diào)用真實(shí)方法
*/
@Test
public void testCallRealMethod() {
when(remoteService.getRemoteNode(anyInt())).thenCallRealMethod(); //設(shè)置調(diào)用真實(shí)方法
Node result = localService.getRemoteNode(1);
assertEquals(1, result.getNum());
assertEquals("Node from remote service", result.getName());
}
重置mock對(duì)象:
//重置mock,清除所有的調(diào)用記錄和返回規(guī)則
Mockito.reset(remoteService);
校驗(yàn)mock對(duì)象0調(diào)用和未被驗(yàn)證的調(diào)用
/**
* 校驗(yàn)mock對(duì)象0調(diào)用和未被驗(yàn)證的調(diào)用
*/
@Test(expected = NoInteractionsWanted.class)
public void testInteraction() {
verifyZeroInteractions(remoteService); //目前還未被調(diào)用過(guò),執(zhí)行不報(bào)錯(cuò)
Node target = new Node(1, "target");
when(remoteService.getRemoteNode(anyInt())).thenReturn(target);
localService.getRemoteNode(1);
localService.getRemoteNode(2);
verify(remoteService, times(2)).getRemoteNode(anyInt());
// 參數(shù)1和2的兩次調(diào)用都會(huì)被上面的anyInt()校驗(yàn)到,所以沒(méi)有未被校驗(yàn)的調(diào)用了
verifyNoMoreInteractions(remoteService);
reset(remoteService);
localService.getRemoteNode(1);
localService.getRemoteNode(2);
verify(remoteService, times(1)).getRemoteNode(1);
// 參數(shù)2的調(diào)用不會(huì)被上面的校驗(yàn)到,所以執(zhí)行會(huì)拋異常
verifyNoMoreInteractions(remoteService);
}
三、PowerMock的使用
PowerMock的使用與Mockito有一些不同,首先是測(cè)試類上的@RunWith注解需要修改為:
@RunWith(PowerMockRunner.class)
第二是需要使用到@PrepareForTest注解(PrepareFotTest注解會(huì)修改傳入?yún)?shù)類的字節(jié)碼,通過(guò)修改字節(jié)碼達(dá)到模擬final、static、私有方法、系統(tǒng)類等的功能),此注解可寫(xiě)在類上也可寫(xiě)在方法上:
@PrepareForTest(RemoteServiceImpl.class)
mock new關(guān)鍵字
//LocalServiceImpl
@Override
public Node getLocalNode(int num, String name) {
return new Node(num, name);
}
/**
* mock new關(guān)鍵字
*/
@Test
@PrepareForTest(LocalServiceImpl.class) //PrepareForTest修改local類的字節(jié)碼以覆蓋new的功能
public void testNew() throws Exception {
Node target = new Node(1, "target");
//當(dāng)傳入任意int且name屬性為"name"時(shí),new對(duì)象返回為target
//當(dāng)參數(shù)條件使用了any系列方法時(shí),剩余的參數(shù)都得使用相應(yīng)的模糊匹配規(guī)則,如eq("name")代表參數(shù)等于"name"
//剩余還有isNull(), isNotNull(), isA()等方法
PowerMockito.whenNew(Node.class).withArguments(anyInt(), eq("name")).thenReturn(target);
Node result = localService.getLocalNode(2, "name");
assertEquals(target, result); //返回值為target
assertEquals(1, result.getNum());
assertEquals("target", result.getName());
//未指定name為"test"的返回值,默認(rèn)返回null
Node result2 = localService.getLocalNode(1, "test");
assertNull(result2);
}
mock final方法
//RemoteServiceImpl
@Override
public final Node getFinalNode() {
return new Node(1, "final node");
}
/**
* mock final方法
*/
@Test
@PrepareForTest(RemoteServiceImpl.class) //final方法在RemoteServiceImpl類中
public void testFinal() {
Node target = new Node(2, "mock");
PowerMockito.when(remoteService.getFinalNode()).thenReturn(target); //指定返回值
Node result = remoteService.getFinalNode(); //直接調(diào)用final方法,返回mock后的值
assertEquals(target, result); //驗(yàn)證返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}
mock static方法
//Node
public static Node getStaticNode() {
return new Node(1, "static node");
}
/**
* mock static方法
*/
@Test
@PrepareForTest(Node.class) //static方法定義在Node類中
public void testStatic() {
Node target = new Node(2, "mock");
PowerMockito.mockStatic(Node.class); //mock static方法前需要加這一句
PowerMockito.when(Node.getStaticNode()).thenReturn(target); //指定返回值
Node result = Node.getStaticNode(); //直接調(diào)用static方法,返回mock后的值
assertEquals(target, result); //驗(yàn)證返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}
mock private方法
//RemoteServiceImpl
@Override
public Node getPrivateNode() {
return privateMethod();
}
//RemoteServiceImpl
private Node privateMethod() {
return new Node(1, "private node");
}
/**
* mock 私有方法
*/
@Test
@PrepareForTest(RemoteServiceImpl.class) //private方法定義在RemoteServiceImpl類中
public void testPrivate() throws Exception {
Node target = new Node(2, "mock");
//按照真實(shí)代碼調(diào)用privateMethod方法
PowerMockito.when(remoteService.getPrivateNode()).thenCallRealMethod();
//私有方法無(wú)法訪問(wèn),類似反射傳遞方法名和參數(shù),此處無(wú)參數(shù)故未傳
PowerMockito.when(remoteService, "privateMethod").thenReturn(target);
Node result = remoteService.getPrivateNode();
assertEquals(target, result); //驗(yàn)證返回值
assertEquals(2, result.getNum());
assertEquals("mock", result.getName());
}
mock 系統(tǒng)類方法
//RemoteServiceImpl
@Override
public Node getSystemPropertyNode() {
return new Node(System.getProperty("abc"));
}
/**
* mock 系統(tǒng)類方法
*/
@Test
@PrepareForTest(RemoteServiceImpl.class) //類似new關(guān)鍵字,系統(tǒng)類方法的調(diào)用在類RemoteServiceImpl中,所以這里填的是RemoteServiceImpl
public void testSystem() {
PowerMockito.mockStatic(System.class); //調(diào)用的是系統(tǒng)類的靜態(tài)方法,所以要加這一句
PowerMockito.when(System.getProperty("abc")).thenReturn("mock"); //設(shè)置System.getProperty("abc")返回"mock"
PowerMockito.when(remoteService.getSystemPropertyNode()).thenCallRealMethod(); //設(shè)置mock對(duì)象調(diào)用實(shí)際方法
Node result = remoteService.getSystemPropertyNode(); //按代碼會(huì)返回一個(gè)name屬性為"mock"的對(duì)象
assertEquals(0, result.getNum()); //int默認(rèn)值為0
assertEquals("mock", result.getName()); //remoteService對(duì)象中調(diào)用System.getProperty("abc")返回的是上面設(shè)置的"mock"
}
項(xiàng)目代碼已上傳至GitHub:MockDemo
總結(jié)
以上是生活随笔為你收集整理的powermockito教程_Mockito与PowerMock的使用基础教程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 操作系统基础:计算机作业管理知识笔记
- 下一篇: 计算机网络基础概念知识笔记