国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

使用 PowerMock 寫單元測試,被坑慘了!

開發 前端
PowerMock 寫單測對開發人員來說確實很方便,但是如果工程中的代碼量比較大,團隊又要求單測覆蓋率高,那單測類的數量確實會很多,最終結果就是單測耗時時間很長。這種情況并不適合使用 PowerMock 框架。

大家好,我是君哥。

最近在工作中遇到一個不太好解決的問題,我負責的系統單元測試跑的非常慢,有時候甚至超過 2 個半小時。

公司要求上線前流水線里面的單測必須全部跑成功。跑流水線的時候如果有單測跑失敗,需要修改后重新跑,又得跑 2 個多小時。極端情況下得反反復復來幾次,真的讓人感到煎熬。有時候發現測試用例跑失敗的原因竟然是 OOM。

今天就來聊一聊造成單測跑的慢的罪魁禍首,PowerMock。

1.PowerMock 基礎

要說 PowerMock 怎么樣,那是真的非常好用。下面列給出幾個示例,先上一段業務代碼,然后我們通過 3 個測試用例把這段代碼單測覆蓋率寫到 100%。

1  public class FileParser {
2  
3      private Logger logger = LoggerFactory.getLogger(getClass());
4  
5      @Resource
6      private UserRepository userRepository;
7  
8      public void parseFile(String fileName) {
9          File file = new File(fileName);
10          if (!file.exists()){
11              return;
12          }
13          try {
14              BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
15              String line = null;
16              while ((line = bufferedReader.readLine())!= null){
17                  User user = userRepository.getUser(line);
18                  logger.info("user with name{}:{}", line, user);
19              }
20         }catch (IOException e){
21             throw new RuntimeException(e);
22         }
23     }
24 }

這段代碼涉及到讀文件、依賴注入、異常處理,我們寫單測也從這三個方面來完成。

1.1 文件不存在

我們先來模擬一下文件不存在,這個用例覆蓋到上面文件不存在的判斷。測試用例如下 :

@Test
public void testParseFile_not_exists() throws Exception {
 File file = PowerMockito.mock(File.class);
 PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(file);
 when(file.exists()).thenReturn(false);
 fileParser.parseFile("123");
 Mockito.verify(userRepository, Mockito.times(0)).getUser(anyString());
}

這里使用 PowerMock 方便地模擬了第 11 行代碼文件不存在,用例成功。

1.2 循環跳出

這段用例要模擬按行讀文件、dao 層查詢用戶、跳出循環這三個代碼,測試用例代碼如下:

@Test
public void testParseFile_exists() throws Exception {
 File file = PowerMockito.mock(File.class);
 PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(file);
 when(file.exists()).thenReturn(true);

 FileInputStream fileInputStream = PowerMockito.mock(FileInputStream.class);
 PowerMockito.whenNew(FileInputStream.class).withAnyArguments().thenReturn(fileInputStream);

 InputStreamReader inputStreamReader = PowerMockito.mock(InputStreamReader.class);
 PowerMockito.whenNew(InputStreamReader.class).withAnyArguments().thenReturn(inputStreamReader);

 BufferedReader bufferedReader = PowerMockito.mock(BufferedReader.class);
 PowerMockito.whenNew(BufferedReader.class).withAnyArguments().thenReturn(bufferedReader);

 //模擬循環和跳出
 when(bufferedReader.readLine()).thenReturn("testUser").thenReturn("user").thenReturn(null);
 User user = PowerMockito.mock(User.class);
 when(userRepository.getUser(anyString())).thenReturn(user);

 fileParser.parseFile("123");

 Mockito.verify(userRepository, Mockito.times(1)).getUser(anyString());
}

這段用例跑完后,已經覆蓋到源代碼的第 17行和 19 行。

1.3 模擬異常

源代碼中有一個異常處理,用例要達到 100% 覆蓋,必須把這個異常用測試用例模擬出來。下面看一下測試用例:

@Test(expected = RuntimeException.class)
public void testParseFile_exception() throws Exception {
    File file = PowerMockito.mock(File.class);
    PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(file);
    when(file.exists()).thenReturn(true);

    FileInputStream fileInputStream = PowerMockito.mock(FileInputStream.class);
    PowerMockito.whenNew(FileInputStream.class).withAnyArguments().thenReturn(fileInputStream);

    InputStreamReader inputStreamReader = PowerMockito.mock(InputStreamReader.class);
    PowerMockito.whenNew(InputStreamReader.class).withAnyArguments().thenReturn(inputStreamReader);

    BufferedReader bufferedReader = PowerMockito.mock(BufferedReader.class);
    PowerMockito.whenNew(BufferedReader.class).withAnyArguments().thenReturn(bufferedReader);

    //模擬拋出異常
    when(bufferedReader.readLine()).thenThrow(new IOException());

    fileParser.parseFile("123");
}

至此,單測覆蓋率達到 100%。

2.PowerMock 進階

下面再來使用幾個 PowerMock 的功能。再來一段示例代碼:

1   public void parseFileWithScanner(String fileName) {
2    File file = new File(fileName);
3    if (!file.exists()){
4     return;
5    }
6    try {
7     Scanner scanner = new Scanner(file);
8     String line = null;
9     while (scanner.hasNextLine()){
10     line = scanner.nextLine();
11     if (StringUtils.equals(line, "testUser")){
12      User user = userRepository.getUser(line);
13      logger.info("user with name{}:{}", line, user);
14     }
15    }
16   }catch (IOException e){
17    throw new RuntimeException(e);
18   }
19  }

這次我們也要增加 2 個用例的 mock,一個是 Scanner 這個 final 類,第二個是 StringUtils 這個靜態類。

2.1 final 類

雖然是一個 final 類,但使用了 PowerMock 框架,我們就像普通類一樣就可以用例。

@Test
public void testParseFile_scanner() throws Exception {
 File file = PowerMockito.mock(File.class);
 PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(file);
 when(file.exists()).thenReturn(true);

 Scanner scanner = PowerMockito.mock(Scanner.class);
 PowerMockito.whenNew(Scanner.class).withAnyArguments().thenReturn(scanner);

 //模擬循環
 when(scanner.hasNextLine()).thenReturn(true).thenReturn(true).thenReturn(false);
 when(scanner.nextLine()).thenReturn("testUser").thenReturn("user");

 User user = PowerMockito.mock(User.class);
 when(userRepository.getUser(anyString())).thenReturn(user);

 fileParser.parseFileWithScanner("123");

 Mockito.verify(userRepository, Mockito.times(1)).getUser(anyString());
}

除了 final 類,抽象類、接口都可以 mock,確實很方便。

2.2 靜態類

PowerMock 可以方便地模擬靜態類,下面這個測試用例對 StringUtils 這個靜態類進行了 mock,每次 equals 方法都是返回 false。

@Test
public void testParseFile_StringUtils() throws Exception {
 File file = PowerMockito.mock(File.class);
 PowerMockito.whenNew(File.class).withAnyArguments().thenReturn(file);
 when(file.exists()).thenReturn(true);

 Scanner scanner = PowerMockito.mock(Scanner.class);
 PowerMockito.whenNew(Scanner.class).withAnyArguments().thenReturn(scanner);

 //模擬循環
 when(scanner.hasNextLine()).thenReturn(true).thenReturn(true).thenReturn(false);
 when(scanner.nextLine()).thenReturn("testUser").thenReturn("user");
 when(StringUtils.equals(anyString(), anyString())).thenReturn(false).thenReturn(false);
 User user = PowerMockito.mock(User.class);
 when(userRepository.getUser(anyString())).thenReturn(user);

 fileParser.parseFileWithScanner("123");

 Mockito.verify(userRepository, Mockito.times(0)).getUser(anyString());
}

因為 equals 方法一直返回 false,所以 getUser 方法沒有執行到,測試用例中 verify getUser 方法被調用 0 次。需要注意的是,模擬靜態類需要在類定義上面加上一個注解,然后對靜態類要做一次 mockStatic。看下面的 @Before 注解。

@RunWith(PowerMockRunner.class)
@PrepareForTest({FileParser.class, StringUtils.class})
public class FileParserTest {

@Before
public void before(){
 PowerMockito.mockStatic(StringUtils.class);
}

3.原因分析

PowerMock 因為使用了 @PrepareForTest、@PowerMockIgnore、@SuppressStaticInitialzationFor 這三個注解,這三個注解的參數值不一樣,會導致每個單測類執行的時候不能復用公有類加載器,而是需要創建一個自己獨有的類加載器。這導致類加載過程十分耗時。

在單測類數量比較少的情況下,單測耗時問題是不會出現的,但是如果一個工程中的單測類數據猛增,比如我們的單測類在 600+,問題就暴露出來的。最難的是不太好做優化,因為如果要去掉 PowerMock 框架,要改造的東西太多了。

4.最后

PowerMock 寫單測對開發人員來說確實很方便,但是如果工程中的代碼量比較大,團隊又要求單測覆蓋率高,那單測類的數量確實會很多,最終結果就是單測耗時時間很長。這種情況并不適合使用 PowerMock 框架。

圖片圖片

同時我們也要看到,PowerMock 最近一次核心代碼更新已經是 4 年前了,單測類數據量多導致的內存問題、耗時問題并沒有解決。所以選型的時候一定要慎重。

責任編輯:武曉燕 來源: 君哥聊技術
相關推薦

2021-05-05 11:38:40

TestNGPowerMock單元測試

2021-03-11 12:33:50

JavaPowerMock技巧

2020-03-20 08:00:32

代碼程序員追求

2021-07-16 07:57:35

SpringBootOpenFeign微服務

2017-01-16 12:12:29

單元測試JUnit

2017-01-14 23:26:17

單元測試JUnit測試

2017-01-14 23:42:49

單元測試框架軟件測試

2020-09-11 16:00:40

Bash單元測試

2021-10-12 19:16:26

Jest單元測試

2023-07-26 08:58:45

Golang單元測試

2017-03-23 16:02:10

Mock技術單元測試

2012-05-17 09:09:05

Titanium單元測試

2013-06-04 09:49:04

Spring單元測試軟件測試

2025-04-22 03:00:00

模型SpringAI

2024-10-16 16:09:32

2010-03-04 15:40:14

Python單元測試

2021-03-28 23:03:50

Python程序員編碼

2025-08-28 01:00:00

Go單元測試

2020-08-18 08:10:02

單元測試Java

2023-08-02 13:59:00

GoogleTestCTest單元測試
點贊
收藏

51CTO技術棧公眾號

奇米影视亚洲狠狠色| 欧美亚洲国产另类| 97视频在线观看视频免费视频| 亚洲自拍偷拍一区二区三区| 色的视频在线免费看| 中文字幕视频在线免费观看| www.成人影院| 日本成人中文字幕| 色呦哟—国产精品| 国产精品夫妻自拍| 国内精品久久影院| 国内自拍在线观看| 日本不卡网站| 日本在线不卡视频| 国产成人精品一区二区在线| 久久国产这里只有精品| 精品淫伦v久久水蜜桃| 久久综合色播五月| 久久精品国产电影| 免费看一级大黄情大片| 99国产精品99久久久久久粉嫩| 欧美影院一区二区| 国产欧美精品一区二区三区| 激情福利在线| 在线成人h网| 日韩写真欧美这视频| 成人欧美一区二区三区黑人免费| 国产免费av在线| 男人的天堂亚洲在线| 91精品婷婷国产综合久久性色 | 国产精品视频1区| 亚洲成人男人天堂| 欧美在线二区| 欧美精品久久久久久久久老牛影院| 好吊色欧美一区二区三区视频| 五月香视频在线观看| 一区二区欧美精品| 99国产高清| 大桥未久在线播放| 99久久国产综合精品色伊| 久久精品国产免费观看| 爱看av在线| 日韩欧美的一区| 狠狠爱免费视频| 欧美亚洲国产一区| 欧美电影在线免费观看| 日色在线视频| 蜜桃av一区二区在线观看| 色偷偷av亚洲男人的天堂| jizzjizzjizz亚洲日本| 激情亚洲网站| 92国产精品视频| 人狥杂交一区欧美二区| 亚洲国产精彩中文乱码av| 手机看片福利盒子久久| 中文字幕一区二区三区在线视频| 欧美一区二区福利在线| av黄色在线网站| 99久久99热这里只有精品 | 视频欧美精品| 一区二区三区日韩在线观看| 国产在线视频欧美一区二区三区| 97久久夜色精品国产| 日韩精品亚洲视频| 色综合小说天天综合网| 在线 亚洲欧美在线综合一区| 97超级碰碰| 成人在线日韩| 欧洲色大大久久| 日本成年人网址| 自拍偷拍亚洲欧美日韩| 中文字幕不卡每日更新1区2区| 亚洲另类春色校园小说| 欧美一级二级三级蜜桃| wwww在线观看免费视频| 亚洲r级在线视频| 看全色黄大色大片| 亚洲字幕久久| 日韩欧美在线观看强乱免费| 美女毛片一区二区三区四区最新中文字幕亚洲 | 97精品国产97久久久久久免费| 国产精品入口久久| 日韩精品视频在线| 中文字幕福利片| 91一区二区三区在线观看| 国产视频手机在线播放| 性欧美疯狂xxxxbbbb| 污污的网站在线看| 亚洲妇女屁股眼交7| 欧美国产视频一区| 国语自产精品视频在线看8查询8| 久久久精品999| 国产欧美日韩在线观看视频| 玛丽玛丽电影原版免费观看1977 | 国内精品**久久毛片app| 老司机精品视频导航| 国产精国产精品| 国产美女一区| 成人国产亚洲精品a区天堂华泰 | 91精品天堂| 成人免费毛片嘿嘿连载视频| 欧美日韩国产一二| 亚洲九九在线| 欧美 日韩 国产精品| 日本亚洲视频在线| 亚洲另类第一页| 精品欧美一区二区三区精品久久| 色哟哟在线观看| 亚洲无线码在线一区观看| 白浆视频在线观看| 7777精品伊人久久久大香线蕉的 | av资源网在线观看| 一二三四社区欧美黄| 在线中文字幕视频观看| 欧美色图一区二区三区| 亚洲aⅴ优女av综合久久久| 亚洲国产日韩欧美在线99| 毛片免费不卡| 欧美理论电影在线| 男人亚洲天堂| 欧美另类99xxxxx| 玖玖精品在线| 国产精品乱码一区二区三区| 狠狠色综合网| 亚洲成人福利在线| 亚洲国产精品久久久久| 亚洲激情中文在线| 成人欧美一区二区三区视频xxx| 北条麻妃国产九九精品视频| 无码人妻精品一区二区蜜桃网站| 午夜影视日本亚洲欧洲精品| 日本中文字幕视频一区| 亚洲日本japanese丝袜| 免费观看在线综合色| 免费观看v片在线观看| 亚洲午夜国产一区99re久久| 宅男午夜在线| 久久高清视频免费| 成人黄色av网址| 国产激情久久久| 99亚偷拍自图区亚洲| av中文字幕在线观看第一页| 精品久久久久久中文字幕动漫 | 日本美女视频一区二区| 欧洲综合视频| 国产精品久久久久av| 国产成人亚洲综合91| 中文字幕乱码亚洲无线精品一区| 激情小说亚洲图片| 91午夜精品| 久久久久午夜电影| 99re8这里有精品热视频免费| 日韩a在线播放| 国产成人精品视频在线| 在线成人免费视频| 日本在线不卡视频一二三区| 久久99精品久久久久久久久久久久 | 亚洲成av人片www| 色婷婷综合久久久久| 久久久91精品| 亚洲福利视频久久| 国产亚洲一区二区三区四区| 欧美精品总汇| 成人黄视频在线观看| 综合欧美一区二区三区| 黑人极品ⅴideos精品欧美棵| 最近中文字幕在线中文视频| 日韩日韩日韩日韩| 欧美wwwww| 黄污在线观看| 国产精品美女av| 久久久久毛片| 国产精品网站免费| 一区二区三区不卡视频在线观看 | 久草在线中文888| 国产乱子伦农村叉叉叉| 爱久久·www| 在线观看免费黄网站| 欧美激情在线观看视频| 亚洲一级影院| 国产精品一区二区三区视频网站| 日本天堂免费a| 欧美色一级片| 欧美中文一区二区| 欧美电影完整版在线观看| 搞黄视频免费在线观看| 日韩成人一级| 成人在线黄色电影| 国产精品亚洲不卡a| 久久成人18免费观看| 黄网站在线播放| 亚洲一区二区三区成人| 波多野结衣在线观看一区二区| 欧美美乳在线| 久久亚洲精品一区二区| 精品一区二区三区在线观看国产| 蜜桃视频在线一区| 亚洲靠逼com| 美日韩精品视频| 欧美大片网站在线观看|