單元測試技術分析及PowerMock技術實踐

1 主流的單元測試技術

     當前互聯網主流的單元測試技術主要有Junit, JMock, Mockito ,PowerMock等框架。

1.1 Junit單元測試框架

      Junit是一套java編寫的開源測試框架IDEA,Eclipse等開發工具均有集成。可以輕鬆的完成依賴關係少或者比較簡單的單元測試,但是對於依賴關係多的場景又非常耗時。比如依賴數據庫,依賴地圖資源,或者依賴第三方的某些組件。很多時候我們需要修改依賴的資源來模擬程序運行的一些場景,卻又無法保證這些依賴資源的正確性。在SpringMVC框架中我們一般只需要測試Controller層和Service即可保證比較高的測試覆蓋率。也能夠測試到程序的主要執行邏輯。爲了擺脫依賴資源對單元測試效率的鉗制,希望能夠直接模擬這些資源,就引入了Mock(模擬)框架。

1.2 Mock類框架對比

        JMock既可以模擬一般方法也可以模式靜態方法等,但是寫法較爲複雜學習成本較高。Mockito 相較於JMock寫法簡單卻不能模擬靜態方法。PowerMock是Mockito 的增強,結合了兩者的優點,引入了靜態方法的Mock特性。卻因爲使用@PrepareForTest註解注入靜態方法導致生成的字節碼和一般測試字節碼有所區別,所以被註解的進來的class無法出現覆蓋率。但是我們一般不會在被測試目標中寫靜態方法,一般會寫在工具類中。所以這些靜態方法不會被覆蓋,覆蓋率會有所降低。當然這三個工具各有利弊,根據需要選擇即可。

2 如何使用PowerMock進行單元測試

       接下來以Maven項目爲例手把手教你搭建PowerMock單元測試demo。

2.1 引入依賴jar包

       在pom文件中添加如下所示的內容:

版本號:

    <powermock.version>1.7.1</powermock.version>    <maven.compiler.source>1.6</maven.compiler.source>    <maven.compiler.target>1.6</maven.compiler.target>    <mockito1.version>1.10.19</mockito1.version>    <mockito2.version>2.8.9</mockito2.version>    <assertj-core.version>3.5.2</assertj-core.version>    <junit.version>4.12</junit.version>

引入jar:

<dependency>      <groupId>org.assertj</groupId>      <artifactId>assertj-core</artifactId>      <version>${assertj-core.version}</version>      <scope>test</scope>    </dependency>    <dependency>      <groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>${junit.version}</version>      <scope>test</scope>    </dependency>    <dependency>      <groupId>org.powermock</groupId>      <artifactId>powermock-api-mockito</artifactId>      <version>${powermock.version}</version>    </dependency>    <dependency>      <groupId>org.powermock</groupId>      <artifactId>powermock-core</artifactId>      <version>${powermock.version}</version>    </dependency>    <dependency>      <groupId>org.powermock</groupId>      <artifactId>powermock-module-junit4</artifactId>      <version>${powermock.version}</version>    </dependency>    <dependency>

2.2 編寫待測試類

      拿我最喜歡的餐館採購功能舉例子,我有一個通過食物種類來獲取餐館採購的食物訂單詳情的方法。cookerService.getFoodList查詢到蔬菜的採購訂單詳情,下面的循環中又將採購種類修改成了水果(真實情況應該沒有這種業務,這裏爲了說明問題)。其中RestuarantUtils.isFood是一個靜態方法。

@GetMapping("foodList")    @ResponseBody    public List<FoodResponseEntity> getFoodList(FoodRequestEntity food) {        FoodResponseEntity foodResponse = new FoodResponseEntity();        List<FoodResponseEntity> FoodList = cookerService.getFoodList("蔬菜");        if(RestuarantUtils.isFood(FoodList)){          for(FoodResponseEntity foodResponseEntity:FoodList){          foodResponseEntity.setFoodKinds("水果");           }        }        return FoodList;    }

2.3 編寫單元測試

第一步:引入PowerMockRunner並且將待Mock的靜態類必須寫到@PrepareForTest中。不要講測試對象寫到該註解,否則jacoco無法做到覆蓋率統計。

@RunWith(PowerMockRunner.class)@PrepareForTest({RestuarantUtils.class})public class CookerControllerTest {

第二步:注入測試對象

  @InjectMocks  CookerController cookerController;

第三步:註解的形式注入模擬的普通對象

 @Mock CookerServiceImpl cookerService;

第四步:必須對註解對象和靜態Mock對象初始化

  @Before    public void setUp() {        // 對定義了註解對象進行初始化        MockitoAnnotations.initMocks(this);        PowerMockito.mockStatic(RestuarantUtils.class);    }

第五步:編寫測試邏輯       

       首先分析要做的操作首先是Mock一個cookerService.getFoodList("蔬菜")的結果,其次是Mock靜態函數RestuarantUtils.isFood(FoodList)的結果。然後執行整個方法獲取結果。然後再和我們預期的結果對比。

 @Test    public void testGetFoodList(){        //假如食物清單全部爲蔬菜        List<FoodResponseEntity> mockFoodList=new ArrayList<>();        FoodResponseEntity foodResponseEntity=new FoodResponseEntity();        foodResponseEntity.setFoodKinds("蔬菜");        mockFoodList.add(foodResponseEntity);        //現在開始Mock這個方法        PowerMockito.when(cookerService.getFoodList("蔬菜")).thenReturn(mockFoodList);        //mock靜態方法        PowerMockito.when(RestuarantUtils.isFood(mockFoodList)).thenReturn(true);        //執行完要返回的清單        List<FoodResponseEntity> returnFoodList=cookerController.getFoodList(null);        Assert.assertEquals(returnFoodList.get(0).getFoodKinds(),"水果");    }

小結:單元測試結果如下測試通過。

        

 

3 單元測試覆蓋率

3.1 maven引入覆蓋率插件

     通過maven引入maven-compiler-plugin插件執行編譯操作,maven-surefire-plugin和jacoco-maven-plugin配合生成覆蓋率。

<build>    <plugins>      <plugin>        <groupId>org.apache.maven.plugins</groupId>        <artifactId>maven-compiler-plugin</artifactId>        <version>3.1</version>        <configuration>          <source>${jdk.version}</source>          <target>${jdk.version}</target>          <encoding>${project.build.sourceEncoding}</encoding>        </configuration>      </plugin>      <plugin>        <groupId>org.apache.maven.plugins</groupId>        <artifactId>maven-surefire-plugin</artifactId>        <version>2.12.4</version>        <configuration>          <skip>false</skip>        </configuration>      </plugin>      <plugin>        <groupId>org.jacoco</groupId>        <artifactId>jacoco-maven-plugin</artifactId>        <version>${jacoco.version}</version>        <executions>          <execution>            <id>prepare-agent</id>            <goals>              <goal>prepare-agent</goal>            </goals>          </execution>          <execution>            <id>report</id>            <phase>prepare-package</phase>            <goals>              <goal>report</goal>            </goals>          </execution>          <execution>            <id>post-unit-test</id>            <phase>test</phase>            <goals>              <goal>report</goal>            </goals>          </execution>        </executions>      </plugin>    </plugins>  </build>

3.2 執行mvn生成覆蓋率文件

       在terminal執行mvn clean install 生成測試文件以及覆蓋率文件。如下圖所示在瀏覽器端可以查看生成的靜態覆蓋率html文件:

(1)執行mvn clean install命令

(2)查看生成的覆蓋率文件

(3)瀏覽器查看

3.3 注意事項

      引入的插件放在在<build>/<plugins>/下,不要在子級pom文件中出現pluginManagement否則mvn clean install命令不會執行覆蓋率插件。

(1)爲什麼網絡上有些人生成覆蓋率配置的命令非常長,本來mvn clean install就能實現的事情,命令中還要出現jar包?

       答:這是因爲有些pom文件忽視了plugins和pluginManagement的區別。plugins 下的 plugin 是真實使用的,而 pluginManagement 下的 plugins 下的 plugin 則僅僅是一種聲明,子項目中可以對 pluginManagement 下的 plugin 進行信息的選擇、繼承、覆蓋等。

(2)執行mvn test命令爲什麼會自動執行單元測試?

      約定俗成,Maven自動去尋找src/test/java下面的類,當此文件夾下面    的類符合以下規範,那麼Maven默認認爲他們是單元測試用例類。

  Test*.java:任何目錄下以Test爲開始的類

  *Test.java: 任何目錄下以Test爲結尾的類

  *TestCase.java: 任何目錄下以TestCase爲結尾的類。

3.4 關於jacoco的在線模式和離線模式

       網絡上有很多文章是對在線模式和離線模式的誤讀,這裏推薦一篇文章詳細的對比了這兩種模式,一般情況下推薦在線模式。

地址:

JAVA代碼覆蓋率工具JaCoCo-原理篇

4 總結

       經過個人的實踐和體會,推薦使用PowerMock和jacoco做單元測試。

 好了,今天的教程就分享到這裏,我是快樂的一隻,一隻快樂的我。如果我的文章對你有所幫助,請隨手點個贊吧,您的鼓勵將是我堅持創作下去最大的動力。

博客地址:

https://blog.csdn.net/renchunlin66

碼雲社區地址:

https://gitee.com/renchunlin66

公衆號請搜索:“快樂的一隻”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章