日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

单元测试之更强大的powermock

發布時間:2024/4/11 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 单元测试之更强大的powermock 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前面一篇說到了Mockito的各種功能,可以幫助我們在編寫測試用例的時候模擬對象的各種行為,但是Mockito對于一些場景還是無法滿足,比方說靜態方法,私有方法(不過一般正常的單元測試很少去mock私有方法),構造方法等

github上關于mockito不支持的地方給了如下說明:

Do not mock types you don’t own
Don’t mock value objects
Don’t mock everything
Show some love with your tests

在FAQ中,也寫了mockito的其他一些問題

Mockito 2.x specific limitations

  • Requires Java 6+
  • Cannot mock static methods
  • Cannot mock constructors
  • Cannot mock equals(), hashCode(). Firstly, you should not mock those methods. Secondly, Mockito defines and depends upon a specific implementation of these methods. Redefining them might break Mockito.
  • Mocking is only possible on VMs that are supported by Objenesis. Don’t worry, most VMs should work just fine.
  • Spying on real methods where real implementation references outer Class via OuterClass.this is impossible. Don’t * worry, this is extremely rare case.

Can I mock static methods?
No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change. If you deal with scary legacy code you can use JMockit or Powermock to mock static methods.

Can I mock private methods?
No. From the standpoint of testing… private methods don’t exist. More about private methods here.

Can I verify toString()?
No. You can stub it, though. Verification of toString() is not implemented mainly because:

When debugging, IDE calls toString() on objects to print local variables and their content, etc. After debugging, the verification of toString() will most likely fail.
toString() is used for logging or during string concatenation. Those invocations are usually irrelevant but they will change the outcome of verification.

對于上述Mockito不能實現的功能,PowerMock可以滿足我們的需求。PowerMock 也是一個單元測試模擬框架,它是在其它單元測試模擬框架的基礎上做出的擴展。通過提供定制的類加載器以及一些字節碼篡改技巧的應用,PowerMock 實現了對靜態方法、構造方法、私有方法以及 Final 方法的Mock支持等強大的功能。目前,PowerMock 僅支持 EasyMock 和 Mockito。

1.依賴jar包

maven項目需要引入如下jar:

<dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>1.7.4</version><scope>test</scope> </dependency> <dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito</artifactId><version>1.7.4</version><scope>test</scope> </dependency>
2.mock靜態方法
public class CommonUtils {public static String getUUID() {String uuid = UUID.randomUUID().toString();uuid = uuid.replace("-", "");return uuid;} } public class UUIDTest {public String getUUId(){return CommonUtils.getUUID();} }

兩個類,一個是靜態工具類,一個是調用工具類的測試類
mock靜態方法的時候就需要使用PowerMockRunner并且添加@PrepareForTest注解,還需要調用對應的mockStatic方法mock靜態方法所在的類

RunWith(PowerMockRunner.class) @PrepareForTest({CommonUtils.class}) public class CommonUtilsTest {@Testpublic void test() throws ParseException {PowerMockito.mockStatic(CommonUtils.class);PowerMockito.when(CommonUtils.getUUID()).thenReturn("12345678910111111");UUIDTest test = new UUIDTest();Assert.assertEquals("12345678910111111",test.getUUId());} }
3.mock私有及final方法
public class TargetClass {public String mockPrivateFunc(int i) {return privateFunc(i + "");}private final String privateFunc(String i) {return "0";} } @RunWith(PowerMockRunner.class) @PrepareForTest({TargetClass.class}) public class MockTest {@Testpublic void testMockPrivateFunc() throws Exception {TargetClass targetClass = PowerMockito.spy(new TargetClass());PowerMockito.when(targetClass,"privateFunc",anyString()).thenReturn("test");String realResult = targetClass.mockPrivateFunc(1);Assert.assertEquals("test", realResult);} }
4.mock構造方法
public class User {private String username;private String password;public User(String username, String password) {this.username = username;this.password = password;}@Overridepublic String toString() {return username+","+password;} } @RunWith(PowerMockRunner.class) //注意下這里添加的是調用構造方法的類而不是User類 @PrepareForTest({UserService.class}) public class UserServiceTest {@Testpublic void saveUser() throws Exception {String username = "mock姓名";String password = "aaa";User user = new User(username, password);UserService userService = new UserService();PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);userService.saveUser("真實姓名","123");}

總結下之里的規律:
如果需要使用PowerMock來mock構造方法,私有方法,final方法和靜態方法,那么都需要使用PowerMockRunner和
@PrepareForTest注解

  • 當使用mock構造方法時,注解@PrepareForTest里寫的類是需要mock的新對象生成的代碼所在的類。
  • 當需要mock系統類的靜態方法的時候,注解里寫的類是需要調用系統方法所在的類
  • 當需要mock final方法,靜態方法,私有方法的時候,注解@PrepareForTest里寫的類是對應方法所在的類
5.Field

@InjectMocks和@Mock注解配合使用可以幫我們做自動注入,在編寫單元測試時也可以使用SpringJUnit4ClassRunner來幫助我們做一些屬性的注入和自動裝配。
但是這樣在單機環境下由于spring容器在啟動的時候會自動完成很多初始化工作,一來比較耗時,二來會去連接一些其他中間件比方說配置中心等,單機下就會出現異常

那么我們就需要PowerMock的field方法來幫助我們做一些裝配的工作

OperateButtonService operateButtonService = new OperateButtonService();Map<String, AbstractGetOperateButton> operateButtonMap = new HashMap<>();operateButtonMap.put("CALL_HOTEL", callHotel); operateButtonMap.put("COMMENT", comment);operateButtonMap.put("RESERVE_AGAIN", reserveAgain);PowerMockito.field(OperateButtonService.class, "operateButtonMap").set(operateButtonService, operateButtonMap);

對operateButtonService的operateButtonMap字段進行賦值,底層原理也是反射,不過powermock幫我們封裝了下,更加容易使用,對于私有變量和靜態變量都可以進行賦值(常量不行)

6.mock靜態void方法 (2020/5/15補充)

使用PowerMock mock靜態void方法,并且通過VerifyStatic驗證調用次數

public class CommonUtils {public static void log(){System.out.println("日志記錄");} } @RunWith(PowerMockRunner.class) @PrepareForTest({CommonUtils.class}) public class CommonUtilsTest {@Testpublic void testStaticVoidMethod() throws ParseException {PowerMockito.mockStatic(CommonUtils.class);PowerMockito.doNothing().when(CommonUtils.class);//這里不是對靜態方法的調用,而是指定了Stub的void方法CommonUtils.log();//這里才是對靜態方法的調用CommonUtils.log();CommonUtils.log();//驗證的時候和上面同理PowerMockito.verifyStatic(CommonUtils.class, Mockito.times(2));CommonUtils.log();} }

參考鏈接:
https://stackoverflow.com/questions/18466198/how-to-verify-static-void-method-has-been-called-with-power-mockito

7.其他注解的功能

@PowerMockRunnerDelegate

如果在使用了PowerMockRunner之后還想使用Spring容器的功能,那么我們就需要spring的runner

@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)

powermock使用了自定義的classloader來解決mock靜態方法與私有方法的問題,因此其會為加了PrepareForTest注解的類生成對應的classloader來加載用到的類,這樣就可能會導致其與系統的classloader加載了相同的類,導致類型轉換失敗,我們可以使用@PowerMockIgnore注解告訴powermock放棄加載指定的這些類

@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})

這也是powermock2.0.0與1.x版本重大不一樣的地方

@SuppressStaticInitializationFor

在單機環境下(無法使用任何外部的資源),因為代碼中我們可能會在static靜態塊或者常量里做一些初始化的操作,比方說提前生成Redis操作的管理類等,此時我們就需要避免這些初始化操作,因為嘗試調用其他服務資源都會失敗

@SuppressStaticInitializationFor(“com.chenpp.RedisManager”)
忽略指定類的靜態初始化, 包括static{}靜態代碼塊和static變量的初始化

PowerMock簡單原理
@RunWith(PowerMockRunner.class) public class UserServiceTest {@Test@PrepareForTest({UserService.class})public void testSaveUser() throws Exception {String username = "mock姓名";String password = "aaa";User user = new User(username, password);Long current = System.currentTimeMillis();UserService userService = new UserService();PowerMockito.whenNew(User.class).withAnyArguments().thenReturn(user);userService.saveUser("真實姓名", "123");System.out.println("testSaveUser:" + userService.getClass().getClassLoader());System.out.println("testSaveUser:" + user.getClass().getClassLoader());System.out.println("testSaveUser:" + System.class.getClassLoader());}@Testpublic void testUser() throws Exception {UserService userService = Mockito.mock(UserService.class, RETURNS_DEEP_STUBS);Mockito.when(userService.getUser().show()).thenReturn("mock test");Assert.assertEquals("mock test", userService.getUser().show());System.out.println("testUser:" + userService.getClass().getClassLoader());System.out.println("testUser:" + User.class.getClassLoader());} }


@RunWith(MockitoJUnitRunner.class) public class UserServiceTest {@Testpublic void testUser() throws Exception {UserService userService = Mockito.mock(UserService.class, RETURNS_DEEP_STUBS);Mockito.when(userService.getUser().show()).thenReturn("mock test");Assert.assertEquals("mock test", userService.getUser().show());System.out.println("testUser:" + userService.getClass().getClassLoader());System.out.println("testUser:" + User.class.getClassLoader());} }


當在某個測試類上使用PowerMockRunner,那么在運行測試用例時,會創建一個新的org.powermock.core.classloader.MockClassLoader類加載器,然后使用該類加載器加載測試用例使用到的類(系統類除外)

PowerMock會根據你的mock要求,去修改寫在注解@PrepareForTest里的class文件(當前測試類會自動加入注解中),以滿足特殊的mock需求。例如:去除final方法的final標識,在靜態方法的最前面加入自己的虛擬實現等。

如果需要mock的是系統類的final方法和靜態方法,PowerMock不會直接修改系統類的class文件,而是修改調用系統類的class文件,以滿足mock需求。

總結

以上是生活随笔為你收集整理的单元测试之更强大的powermock的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。