PowerMock介紹

【原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明原文章地址,謝謝!】

16180055_YaY3.png
一、為什么要使用Mock工具

在做單元測(cè)試的時(shí)候,我們會(huì)發(fā)現(xiàn)我們要測(cè)試的方法會(huì)引用很多外部依賴的對(duì)象,比如:(發(fā)送郵件,網(wǎng)絡(luò)通訊,遠(yuǎn)程服務(wù), 文件系統(tǒng)等等)。 而我們沒法控制這些外部依賴的對(duì)象,為了解決這個(gè)問題,我們就需要用到Mock工具來模擬這些外部依賴的對(duì)象,來完成單元測(cè)試。

二、PowerMock簡(jiǎn)介

PowerMock 也是一個(gè)單元測(cè)試模擬框架,它是在其它單元測(cè)試模擬框架的基礎(chǔ)上做出的擴(kuò)展。通過提供定制的類加載器以及一些字節(jié)碼篡改技巧的應(yīng)用,PowerMock 現(xiàn)了對(duì)靜態(tài)方法、構(gòu)造方法、私有方法以及 Final 方法的模擬支持,對(duì)靜態(tài)初始化過程的移除等強(qiáng)大的功能。因?yàn)?PowerMock 在擴(kuò)展功能時(shí)完全采用和被擴(kuò)展的框架相同的 API, 熟悉 PowerMock 所支持的模擬框架的開發(fā)者會(huì)發(fā)現(xiàn) PowerMock 非常容易上手。PowerMock 的目的就是在當(dāng)前已經(jīng)被大家所熟悉的接口上通過添加極少的方法和注釋來實(shí)現(xiàn)額外的功能。

三、環(huán)境配置

如果是使用 Eclipse 開發(fā),只需要在 Eclipse 工程中包含相關(guān)庫文件即可。
如果是使用 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入門

PowerMock有兩個(gè)重要的注解:

 @RunWith(PowerMockRunner.class)
 @PrepareForTest( { YourClassWithEgStaticMethod.class })

如果你的測(cè)試用例里沒有使用注解 @PrepareForTest,那么可以不用加注解 @RunWith(PowerMockRunner.class),反之亦然。當(dāng)你需要使用PowerMock強(qiáng)大功能(Mock靜態(tài)、final、私有方法等)的時(shí)候,就需要加注解 @PrepareForTest。

五、PowerMock基本用法

模擬 Static 方法
首先,我們需要有一個(gè)含有 static 方法的代碼,如下所示:

package com._520it.test01;
public class IdGenerator {
  public static long generateNewId() { 
    return 0L;
  } 
}

然后,需要在在被測(cè)試代碼中調(diào)用上面的方法,測(cè)試代碼如下所示:

package com._520it.test01;
public class ClassUnderTest {
    public long methodToTest() { 
          final long id = IdGenerator.generateNewId(); 
          return id;
    } 
}

為了測(cè)試各種情況,我們需要讓靜態(tài)方法generateNewId()返回不同的值來對(duì)被測(cè)試的方法methodToTest()的覆蓋測(cè)試,實(shí)現(xiàn)方式如下:

package com._520it.test01;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(IdGenerator.class)
public class TestStatic {
// 模擬 Static 方法
    @Test
    public void testCallInternalInstance() throws Exception {
          PowerMockito.mockStatic(IdGenerator.class);
          // 在這個(gè)測(cè)試用例中,當(dāng)generateNewId()每次被調(diào)用時(shí),都會(huì)返回15
          PowerMockito.when(IdGenerator.generateNewId()).thenReturn(15L);
          Assert.assertEquals(15L, new ClassUnderTest().methodToTest());
          //驗(yàn)證generateNewId()方法是否被調(diào)用
          PowerMockito.verifyStatic(); 
          IdGenerator.generateNewId(); 
  }
}

模擬構(gòu)造函數(shù)
有時(shí)候,可以很好的模擬構(gòu)造函數(shù),從而使被測(cè)代碼中 new 操作返回的對(duì)象可以被隨意定制,會(huì)很大程度的提高單元測(cè)試的效率,測(cè)試代碼如下所示:

package com._520it.test02;
import java.io.File;
public class ClassUnderTest {
      public boolean createDirectoryStructure(String directoryPath) {
            File directory = new File(directoryPath);
            if (directory.exists()) {
                  String msg = "\"" + directoryPath + "\" 已經(jīng)存在.";
                  throw new IllegalArgumentException(msg);
            }
            return directory.mkdirs();
      }
}

為了充分測(cè)試 createDirectoryStructure()函數(shù),我們需要被 new 出來的 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ú)立開來:使用 PowerMock 來模擬 File 類的構(gòu)造函數(shù),使其返回指定的模擬 File 對(duì)象而不是實(shí)際的 File 對(duì)象,然后只需要通過修改指定的模擬 File 對(duì)象的實(shí)現(xiàn),即可實(shí)現(xiàn)對(duì)被測(cè)試代碼的覆蓋測(cè)試.測(cè)試用例如下圖所示:

package com._520it.test02;
import static org.junit.Assert.*;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.verifyNew;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import java.io.File;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class) 
@PrepareForTest(ClassUnderTest.class) 
public class TestConstruction { 
    //模擬構(gòu)造函數(shù)
    @Test 
    public void createDirectoryStructureWhenPathDoesntExist() throws Exception { 
            final String directoryPath = "seemygod"; 
            //創(chuàng)建File的模擬對(duì)象
            File directoryMock = mock(File.class); 
            //在當(dāng)前測(cè)試用例下,當(dāng)出現(xiàn)new File("seemygod")時(shí),就返回模擬對(duì)象
            whenNew(File.class).withArguments(directoryPath).thenReturn(directoryMock); 
            //當(dāng)調(diào)用模擬對(duì)象的exists時(shí),返回false
            when(directoryMock.exists()).thenReturn(false); 
            //當(dāng)調(diào)用模擬對(duì)象的mkdirs時(shí),返回true
            when(directoryMock.mkdirs()).thenReturn(true); 
            assertTrue(new ClassUnderTest().createDirectoryStructure(directoryPath)); 
            //驗(yàn)證new File(directoryPath);  是否被調(diào)用過
            verifyNew(File.class).withArguments(directoryPath); 
    } 
}

模擬私有以及 Final 方法

類的私有方法和Final方法的測(cè)試則需要用到局部模擬.
在使用局部模擬,被創(chuàng)建出來的模擬對(duì)象依然是原系統(tǒng)對(duì)象,被 When().thenReturn()指定的函數(shù)將返回指定的值,沒有指定的函數(shù)將按原有的方式執(zhí)行.測(cè)試代碼如下圖所示:

package com._520it.test03;
public class PrivatePartialMockingExample {
    public String methodToTest() {
            return methodToMock("input");
    }
    private String methodToMock(String input) {
            return "REAL VALUE = " + input;
    }
}

測(cè)試用例:

package com._520it.test03;
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.*;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@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());
            when(underTest, nameOfMethodToMock, input).thenReturn(expected);
            assertEquals(expected, underTest.methodToTest());
            verifyPrivate(underTest).invoke(nameOfMethodToMock, input);
    }
}
更多的Mock方法

六、PowerMock簡(jiǎn)單實(shí)現(xiàn)原理

1.當(dāng)某個(gè)測(cè)試方法被注解@PrepareForTest標(biāo)注以后,在運(yùn)行測(cè)試用例時(shí),會(huì)創(chuàng)建一個(gè)新的org.powermock.core.classloader.MockClassLoader實(shí)例,然后加載該測(cè)試用例使用到的類(系統(tǒng)類除外)。

2.PowerMock會(huì)根據(jù)你的mock要求,去修改寫在注解@PrepareForTest里的class文件(當(dāng)前測(cè)試類會(huì)自動(dòng)加入注解中),以滿足特殊的mock需求。例如:去除'final方法的final標(biāo)識(shí),在靜態(tài)方法的最前面加入自己的虛擬實(shí)現(xiàn)等。

3.如果需要mock的是系統(tǒng)類的final方法和靜態(tài)方法,PowerMock不會(huì)直接修改系統(tǒng)類的class文件,而是修改調(diào)用系統(tǒng)類的class文件,以滿足mock需求。

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容