PowerMock 简介--转载
原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-powermock/
EasyMock 以及 Mockito 都因?yàn)榭梢詷O大地簡(jiǎn)化單元測(cè)試的書寫過(guò)程而被許多人應(yīng)用在自己的工作中,但是這 2 種 Mock 工具都不可以實(shí)現(xiàn)對(duì)靜態(tài)函數(shù)、構(gòu)造函數(shù)、私有函數(shù)、Final 函數(shù)以及系統(tǒng)函數(shù)的模擬,但是這些方法往往是我們?cè)诖笮拖到y(tǒng)中需要的功能。PowerMock 是在 EasyMock 以及 Mockito 基礎(chǔ)上的擴(kuò)展,通過(guò)定制類加載器等技術(shù),PowerMock 實(shí)現(xiàn)了之前提到的所有模擬功能,使其成為大型系統(tǒng)上單元測(cè)試中的必備工具。
單元測(cè)試模擬框架的功能及其實(shí)現(xiàn)簡(jiǎn)介
單元測(cè)試在軟件開發(fā)過(guò)程中的重要性不言而喻,特別是在測(cè)試驅(qū)動(dòng)開發(fā)的開發(fā)模式越來(lái)越流行的前提下,單元測(cè)試更成為了軟件開發(fā)過(guò)程中不可或缺的部分。于是相應(yīng)的,各種單元測(cè)試技術(shù)也應(yīng)運(yùn)而生。本文要介紹的 PowerMock 以及 Mockito 都是簡(jiǎn)化單元測(cè)試書寫過(guò)程的工具。
Mockito 是一個(gè)針對(duì) Java 的單元測(cè)試模擬框架,它與 EasyMock 和 jMock 很相似,都是為了簡(jiǎn)化單元測(cè)試過(guò)程中測(cè)試上下文 ( 或者稱之為測(cè)試驅(qū)動(dòng)函數(shù)以及樁函數(shù) ) 的搭建而開發(fā)的工具。在有這些模擬框架之前,為了編寫某一個(gè)函數(shù)的單元測(cè)試,程序員必須進(jìn)行十分繁瑣的初始化工作,以保證被測(cè)試函數(shù)中使用到的環(huán)境變量以及其他模塊的接口能返回預(yù)期的值,有些時(shí)候?yàn)榱藛卧獪y(cè)試的可行性,甚至需要犧牲被測(cè)代碼本身的結(jié)構(gòu)。單元測(cè)試模擬框架則極大的簡(jiǎn)化了單元測(cè)試的編寫過(guò)程:在被測(cè)試代碼需要調(diào)用某些接口的時(shí)候,直接模擬一個(gè)假的接口,并任意指定該接口的行為。這樣就可以大大的提高單元測(cè)試的效率以及單元測(cè)試代碼的可讀性。
相對(duì)于 EasyMock 和 jMock,Mockito 的優(yōu)點(diǎn)是通過(guò)在執(zhí)行后校驗(yàn)?zāi)男┖瘮?shù)已經(jīng)被調(diào)用,消除了對(duì)期望行為(expectations)的需要。其它的 mocking 庫(kù)需要在執(zhí)行前記錄期望行為(expectations),而這導(dǎo)致了丑陋的初始化代碼。
但是,Mockito 也并不是完美的,它不提供對(duì)靜態(tài)方法、構(gòu)造方法、私有方法以及 Final 方法的模擬支持。而程序員時(shí)常都會(huì)發(fā)現(xiàn)自己有對(duì)以上這些方法的模擬需求,特別是當(dāng)一個(gè)已有的軟件系統(tǒng)擺在面前時(shí)。幸好 , 還有 PowerMock。
PowerMock 也是一個(gè)單元測(cè)試模擬框架,它是在其它單元測(cè)試模擬框架的基礎(chǔ)上做出的擴(kuò)展。通過(guò)提供定制的類加載器以及一些字節(jié)碼篡改技巧的應(yīng)用,PowerMock 現(xiàn)了對(duì)靜態(tài)方法、構(gòu)造方法、私有方法以及 Final 方法的模擬支持,對(duì)靜態(tài)初始化過(guò)程的移除等強(qiáng)大的功能。因?yàn)?PowerMock 在擴(kuò)展功能時(shí)完全采用和被擴(kuò)展的框架相同的 API, 熟悉 PowerMock 所支持的模擬框架的開發(fā)者會(huì)發(fā)現(xiàn) PowerMock 非常容易上手。PowerMock 的目的就是在當(dāng)前已經(jīng)被大家所熟悉的接口上通過(guò)添加極少的方法和注釋來(lái)實(shí)現(xiàn)額外的功能,目前,PowerMock 僅支持 EasyMock 和 Mockito。
本文的目的就是和大家一起學(xué)習(xí)在 Mockito 框架上擴(kuò)展的 PowerMock 的強(qiáng)大功能。
環(huán)境配置方法
對(duì)于需要的開發(fā)包,PowerMock 網(wǎng)站提供了”一站式”下載 : 從?此頁(yè)面中選擇以類似 PowerMock 1.4.10 with Mockito and JUnit including dependencies 為注釋的鏈接,該包中包含了最新的 JUnit 庫(kù),Mockito 庫(kù),PowerMock 庫(kù)以及相關(guān)的依賴。
如果是使用 Eclipse 開發(fā),只需要在 Eclipse 工程中包含這些庫(kù)文件即可。
如果是使用 Maven 開發(fā),則需要根據(jù)版本添加以下清單內(nèi)容到 POM 文件中:
JUnit 版本 4.4 以上請(qǐng)參考清單 1,
清單 1
<properties> <powermock.version>1.4.10</powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies>JUnit 版本 4.0-4.3 請(qǐng)參考清單 2,
清單 2
<properties> <powermock.version>1.4.10</powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4-legacy</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies>JUnit 版本 3 請(qǐng)參考清單 3,
清單 3
<properties> <powermock.version>1.4.10</powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit3</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies>PowerMock 在單元測(cè)試中的應(yīng)用
模擬 Static 方法
在任何需要用到 PowerMock 的類開始之前,首先我們要做如下聲明:
@RunWith(PowerMockRunner.class)
然后,還需要用注釋的形式將需要測(cè)試的靜態(tài)方法提供給 PowerMock:
@PrepareForTest( { YourClassWithEgStaticMethod.class })
然后就可以開始寫測(cè)試代碼:
首先,需要有一個(gè)含有 static 方法的代碼 , 如清單 4:
清單 4
public class IdGenerator { ... public static long generateNewId() { ... } ... }然后,在被測(cè)代碼中,引用了以上方法 , 如清單 5 所示:
清單 5
public class ClassUnderTest { ... public void methodToTest() { .. final long id = IdGenerator.generateNewId(); .. } ... }為了達(dá)到單元測(cè)試的目的,需要讓靜態(tài)方法?generateNewId()返回各種值來(lái)達(dá)到對(duì)被測(cè)試方法?methodToTest()的覆蓋測(cè)試,實(shí)現(xiàn)方式如清單 6 所示:
清單 6
@RunWith(PowerMockRunner.class) //We prepare the IdGenerator for test because the static method is normally not mockable @PrepareForTest(IdGenerator.class) public class MyTestClass { @Test public void demoStaticMethodMocking() throws Exception { mockStatic(IdGenerator.class); /* * Setup the expectation using the standard Mockito syntax, * generateNewId() will now return 2 everytime it's invoked * in this test. */ when(IdGenerator.generateNewId()).thenReturn(2L); new ClassUnderTest().methodToTest(); // Optionally verify that the static method was actually called verifyStatic(); IdGenerator.generateNewId(); } }如清單 6 中所展示,在測(cè)試代碼中,可以使用?When().thenReturn() 語(yǔ)句來(lái)指定被引用的靜態(tài)方法返回任意需要的值,達(dá)到覆蓋測(cè)試的效果。
模擬構(gòu)造函數(shù)
有時(shí)候,能模擬構(gòu)造函數(shù),從而使被測(cè)代碼中?new?操作返回的對(duì)象可以被隨意定制,會(huì)很大程度的提高單元測(cè)試的效率,考慮如清單 7 的代碼:
清單 7
public class DirectoryStructure { public boolean create(String directoryPath) { File directory = new File(directoryPath); if (directory.exists()) { throw new IllegalArgumentException("\"" + directoryPath + "\" already exists."); } return directory.mkdirs(); } }為了充分測(cè)試?create()函數(shù),我們需要被?new?出來(lái)的 File 對(duì)象返回文件存在和不存在兩種結(jié)果。在 PowerMock 出現(xiàn)之前,實(shí)現(xiàn)這個(gè)單元測(cè)試的方式通常都會(huì)需要在實(shí)際的文件系統(tǒng)中去創(chuàng)建對(duì)應(yīng)的路徑以及文件。然而,在 PowerMock 的幫助下,本函數(shù)的測(cè)試可以和實(shí)際的文件系統(tǒng)徹底獨(dú)立開來(lái):使用 PowerMock 來(lái)模擬 File 類的構(gòu)造函數(shù),使其返回指定的模擬 File 對(duì)象而不是實(shí)際的 File 對(duì)象,然后只需要通過(guò)修改指定的模擬 File 對(duì)象的實(shí)現(xiàn),即可實(shí)現(xiàn)對(duì)被測(cè)試代碼的覆蓋測(cè)試,參考如清單 8 的代碼:
清單 8
@RunWith(PowerMockRunner.class) @PrepareForTest(DirectoryStructure.class) public class DirectoryStructureTest { @Test public void createDirectoryStructureWhenPathDoesntExist() throws Exception { final String directoryPath = "mocked path"; File directoryMock = mock(File.class); // This is how you tell PowerMockito to mock construction of a new File. whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock); // Standard expectations when(directoryMock.exists()).thenReturn(false); when(directoryMock.mkdirs()).thenReturn(true); assertTrue(new NewFileExample().createDirectoryStructure(directoryPath)); // Optionally verify that a new File was "created". verifyNew(File.class).withArguments(directoryPath); } }使用?whenNew().withArguments().thenReturn()?語(yǔ)句即可實(shí)現(xiàn)對(duì)具體類的構(gòu)造函數(shù)的模擬操作。然后對(duì)于之前創(chuàng)建的模擬對(duì)象directoryMock使用?When().thenReturn()?語(yǔ)句,即可實(shí)現(xiàn)需要的所有功能,從而實(shí)現(xiàn)對(duì)被測(cè)對(duì)象的覆蓋測(cè)試。在本測(cè)試中,因?yàn)閷?shí)際的模擬操作是在類?DirectoryStructureTest?中實(shí)現(xiàn),所以需要指定的?@PrepareForTest?對(duì)象是?DirectoryStructureTest.class。
模擬私有以及 Final 方法
為了實(shí)現(xiàn)對(duì)類的私有方法或者是 Final 方法的模擬操作,需要 PowerMock 提供的另外一項(xiàng)技術(shù):局部模擬。
在之前的介紹的模擬操作中,我們總是去模擬一整個(gè)類或者對(duì)象,然后使用?When().thenReturn()語(yǔ)句去指定其中值得關(guān)心的部分函數(shù)的返回值,從而達(dá)到搭建各種測(cè)試環(huán)境的目標(biāo)。對(duì)于沒有使用?When().thenReturn()方法指定的函數(shù),系統(tǒng)會(huì)返回各種類型的默認(rèn)值(具體值可參考官方文檔)。
局部模擬則提供了另外一種方式,在使用局部模擬時(shí),被創(chuàng)建出來(lái)的模擬對(duì)象依然是原系統(tǒng)對(duì)象,雖然可以使用方法?When().thenReturn()來(lái)指定某些具體方法的返回值,但是沒有被用此函數(shù)修改過(guò)的函數(shù)依然按照系統(tǒng)原始類的方式來(lái)執(zhí)行。
這種局部模擬的方式的強(qiáng)大之處在于,除開一般方法可以使用之外,Final 方法和私有方法一樣可以使用。
參考如清單 9 所示的被測(cè)代碼:
清單 9
public final class PrivatePartialMockingExample { public String methodToTest() { return methodToMock("input"); } private String methodToMock(String input) { return "REAL VALUE = " + input; } }為了保持單元測(cè)試的純潔性,在測(cè)試方法?methodToTest()時(shí),我們不希望受到私有函數(shù)?methodToMock()實(shí)現(xiàn)的干擾,為了達(dá)到這個(gè)目的,我們使用剛提到的局部模擬方法來(lái)實(shí)現(xiàn) , 實(shí)現(xiàn)方式如清單 10:
清單 10
@RunWith(PowerMockRunner.class) @PrepareForTest(PrivatePartialMockingExample.class) public class PrivatePartialMockingExampleTest { @Test public void demoPrivateMethodMocking() throws Exception { final String expected = "TEST VALUE"; final String nameOfMethodToMock = "methodToMock"; final String input = "input"; PrivatePartialMockingExample underTest = spy(new PrivatePartialMockingExample()); /* * Setup the expectation to the private method using the method name */ when(underTest, nameOfMethodToMock, input).thenReturn(expected); assertEquals(expected, underTest.methodToTest()); // Optionally verify that the private method was actually called verifyPrivate(underTest).invoke(nameOfMethodToMock, input); } }可以發(fā)現(xiàn),為了實(shí)現(xiàn)局部模擬操作,用來(lái)創(chuàng)建模擬對(duì)象的函數(shù)從?mock()?變成了?spy(),操作對(duì)象也從類本身變成了一個(gè)具體的對(duì)象。同時(shí),When()?函數(shù)也使用了不同的版本:在模擬私有方法或者是 Final 方法時(shí),When()?函數(shù)需要依次指定模擬對(duì)象、被指定的函數(shù)名字以及針對(duì)該函數(shù)的輸入?yún)?shù)列表。
結(jié)束語(yǔ)
以上列舉了擴(kuò)展于 Mockito 版本的 PowerMock 的一部分強(qiáng)大的功能,特別是針對(duì)已有的軟件系統(tǒng),利用以上功能可以輕易的完成清晰獨(dú)立的單元測(cè)試代碼,幫助我們提高代碼質(zhì)量。
注:
本文中的部分測(cè)試代碼引用自 Johan Haleby 的?Untestable code with Mockito and PowerMock。
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/5458993.html
總結(jié)
以上是生活随笔為你收集整理的PowerMock 简介--转载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: jackson反序列化时忽略不需要的字段
- 下一篇: publishing failed wi