有了它(powerMocker)再也不怕單元測試不達(dá)標(biāo)了!
作者丨java金融
來源丨java金融
為什么要寫單元測試
優(yōu)點:單元測試可以減少bug率,提升代碼的質(zhì)量。還可以通過單元測試來熟悉業(yè)務(wù)。 公司硬性要求:有些公司可能還會強制要求,每次新增代碼、或者變更代碼單測覆蓋率要達(dá)到多少比例才能申請代碼合并請求。
選擇哪個單元測試框架
目前應(yīng)用比較普遍的java單元測試工具junit4+Mock(Mockito、jmock、EasyMock、powermock)。為什么會選powermock?
在做單元測試的時候,我們會發(fā)現(xiàn)我們要測試的方法會有很多外部依賴的對象或者一些其他服務(wù)的調(diào)用比如說(發(fā)送郵件,網(wǎng)絡(luò)通訊,soa調(diào)用)。而我們沒法控制這些外部依賴的對象。為了解決這個問題,我們需要用到Mock來模擬這些外部依賴的對象,從而控制它們。只關(guān)心我們自己的業(yè)務(wù)邏輯是否正確。而這時powermock就起作用了,它不僅可以mock外部的依賴,還可以mock私有方法、final方法,總之它的功能很強大。
什么是powerMocker
PowerMock是一個框架,它以更強大的功能擴展了其他模擬庫,例如EasyMock。PowerMock使用自定義的類加載器和字節(jié)碼操作來模擬靜態(tài)方法,構(gòu)造函數(shù), 最終類和方法,私有方法,刪除靜態(tài)初始化程序等。通過使用自定義類加載器,無需對IDE或持續(xù)集成服務(wù)器進(jìn)行任何更改,從而簡化了采用過程。熟悉受支持的模擬框架的開發(fā)人員會發(fā)現(xiàn)PowerMock易于使用,因為整個期望API都是相同的, 無論是靜態(tài)方法還是構(gòu)造函數(shù)。PowerMock 旨在通過少量方法和注釋擴展現(xiàn)有的API,以啟用額外的功能。
常用注解
@RunWith(PowerMockRunner.class) 告訴JUnit使用PowerMockRunner進(jìn)行測試。 @PrepareForTest({DemoDao.class}) 所有需要測試的類列在此處,適用于模擬final類或有final, private, static, native方法的類。 @PowerMockIgnore({"javax.management.", "javax.net.ssl."}) 為了解決使用powermock后,提示classloader錯誤。 @SuppressStaticInitializationFor 不讓靜態(tài)代碼加載 其他更多注解可以參考:https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior。
如何開始
JUnit 4.4及以上
<properties>
<powermock.version>2.0.2</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-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
powerMock樣例
這是一個需要被mock的類,里面有私有方法、靜態(tài)方法等等下面就用這個類來演示各個方法的mock功能。
/**
*
* @Date: 2020/3/31
* @Description:
*/
@Repository
public class DemoDao {
public String mockPublicMethod(String type) throws Throwable {
throw new Throwable();
}
public final String mockFinalMethod(String type) throws Throwable {
throw new Throwable();
}
public static String mockStaticMethod(String type) throws Throwable {
throw new Throwable();
}
}
/**
* @Date: 2020/3/31
* @Description:
*/
@Component
public class DemoService extends AbstractDemo{
@Autowired
private DemoDao demoDao;
public String mockPublicMethod() throws Throwable {
return demoDao.mockPublicMethod("demo");
}
public String mockFinalMethod() throws Throwable {
return demoDao.mockFinalMethod("demo");
}
public String mockStaticMethod() throws Throwable {
return DemoDao.mockStaticMethod("demo");
}
private String callPrivateMethod(String type) {
return type;
}
public String mockPublicMethodCallPrivateMethod(String type) throws Throwable {
return callPrivateMethodThrowable(type);
}
private String callPrivateMethodThrowable(String type) throws Throwable {
throw new Throwable();
}
public String mockExtendMethod(String type) throws Throwable {
return getExtendMethod();
}
public static String UUID = "uuid";
}
mock普通公共方法
/**
* @Date: 2020/4/24
* @Description:
*/
@RunWith(PowerMockRunner.class)
public class DemoServiceTest {
@InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao;
/**
* mock 普通方法
* @throws Throwable
*/
@Test
public void mockPublicMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.when(demoDao.mockPublicMethod(any())).thenReturn(type);
String result = demoService.mockPublicMethod();
Assert.assertEquals(type, result);
}
mock Final方法
跟普通方法是一樣的,唯一的區(qū)別是需要在類上加PrepareForTest注解
@RunWith(PowerMockRunner.class)
@PrepareForTest(DemoDao.class)
public class DemoServiceTest {
@InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao;
/**
* mock final方法
* @throws Throwable
*/
@Test
public void mockFinalMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.when(demoDao.mockFinalMethod(any())).thenReturn(type);
String result = demoService.mockFinalMethod();
Assert.assertEquals(type, result);
}
mock靜態(tài)方法(使用 PowerMockito.mockStatic)被mock的類也要用PrepareForTest注解修飾。
@RunWith(PowerMockRunner.class)
@PrepareForTest(DemoDao.class)
public class DemoServiceTest {
@InjectMocks
private DemoService demoService;
@Mock
private DemoDao demoDao;
/**
* mock 靜態(tài)方法
* @throws Throwable
*/
@Test
public void mockStaticMethod() throws Throwable {
String type = UUID.randomUUID().toString();
PowerMockito.mockStatic(DemoDao.class);
PowerMockito.when(DemoDao.mockStaticMethod(any())).thenReturn(type);
String result = demoService.mockStaticMethod();
Assert.assertEquals(type, result);
}
調(diào)用 private方法
/**
* 調(diào)用私有方法
*
* @throws Throwable
*/
/**
* 調(diào)用私有方法
*
* @throws Throwable
*/
@Test
public void callPrivateMethod() throws Throwable {
// 第一種方式
String type = UUID.randomUUID().toString();
Method method = PowerMockito.method(DemoService.class, "callPrivateMethod", String.class);
String result = (String) method.invoke(demoService, type);
Assert.assertEquals(type, result);
//第二種方式
String result1 = Whitebox.invokeMethod(demoService, "callPrivateMethod", type);
Assert.assertEquals(type, result1);
}
mock 私有方法(被mock的類也要用PrepareForTest注解修飾。)
/**
* mock私有方法
*
* @throws Throwable
*/
@Test
public void mockPrivateMethod() throws Throwable {
String type = UUID.randomUUID().toString();
// 重點這一句
demoService = PowerMockito.spy(demoService);
PowerMockito.doReturn(type).when(demoService,"callPrivateMethodThrowable",type);
String result = demoService.mockPublicMethodCallPrivateMethod(type);
Assert.assertEquals(type, result);
}
mock父類方法
/**
* mock父類方法
*
* @throws Throwable
*/
@Test
public void mockExtendMethod() throws Throwable {
String type = UUID.randomUUID().toString();
// 需要mock的父類的方法
Method method = PowerMockito.method(AbstractDemo.class, "getExtendMethod");
// InvocationHandler
PowerMockito.replace(method).with((proxy, method1, args) -> type);
String result = demoService.mockExtendMethod(type);
Assert.assertEquals(type, result);
}
mock構(gòu)造方法
public DemoService() {
throw new NullPointerException();
}
@Test
public void mockConstructorMethod() throws Throwable {
PowerMockito.whenNew(DemoService.class).withNoArguments().thenReturn(demoService);
}
mock字段
/**
* mock 字段
*/
@Test
public void mockFiled(){
String uuid = UUID.randomUUID().toString();
Whitebox.setInternalState(DemoService.class, "UUID",uuid);
Assert.assertEquals(DemoService.UUID, uuid);
}
結(jié)束
由于自己才疏學(xué)淺,難免會有紕漏,假如你發(fā)現(xiàn)了錯誤的地方,還望留言給我指出來,我會對其加以修正。 如果你覺得文章還不錯,你的轉(zhuǎn)發(fā)、分享、贊賞、點贊、留言就是對我最大的鼓勵。 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取