有了它(powerMocker)再也不怕單元測(cè)試不達(dá)標(biāo)了!
作者丨java金融
來源丨java金融
為什么要寫單元測(cè)試
優(yōu)點(diǎn):?jiǎn)卧獪y(cè)試可以減少bug率,提升代碼的質(zhì)量。還可以通過單元測(cè)試來熟悉業(yè)務(wù)。 公司硬性要求:有些公司可能還會(huì)強(qiáng)制要求,每次新增代碼、或者變更代碼單測(cè)覆蓋率要達(dá)到多少比例才能申請(qǐng)代碼合并請(qǐng)求。
選擇哪個(gè)單元測(cè)試框架
目前應(yīng)用比較普遍的java單元測(cè)試工具junit4+Mock(Mockito、jmock、EasyMock、powermock)。為什么會(huì)選powermock?
在做單元測(cè)試的時(shí)候,我們會(huì)發(fā)現(xiàn)我們要測(cè)試的方法會(huì)有很多外部依賴的對(duì)象或者一些其他服務(wù)的調(diào)用比如說(發(fā)送郵件,網(wǎng)絡(luò)通訊,soa調(diào)用)。而我們沒法控制這些外部依賴的對(duì)象。為了解決這個(gè)問題,我們需要用到Mock來模擬這些外部依賴的對(duì)象,從而控制它們。只關(guān)心我們自己的業(yè)務(wù)邏輯是否正確。而這時(shí)powermock就起作用了,它不僅可以mock外部的依賴,還可以mock私有方法、final方法,總之它的功能很強(qiáng)大。
什么是powerMocker
PowerMock是一個(gè)框架,它以更強(qiáng)大的功能擴(kuò)展了其他模擬庫(kù),例如EasyMock。PowerMock使用自定義的類加載器和字節(jié)碼操作來模擬靜態(tài)方法,構(gòu)造函數(shù), 最終類和方法,私有方法,刪除靜態(tài)初始化程序等。通過使用自定義類加載器,無需對(duì)IDE或持續(xù)集成服務(wù)器進(jìn)行任何更改,從而簡(jiǎn)化了采用過程。熟悉受支持的模擬框架的開發(fā)人員會(huì)發(fā)現(xiàn)PowerMock易于使用,因?yàn)檎麄€(gè)期望API都是相同的, 無論是靜態(tài)方法還是構(gòu)造函數(shù)。PowerMock 旨在通過少量方法和注釋擴(kuò)展現(xiàn)有的API,以啟用額外的功能。
常用注解
@RunWith(PowerMockRunner.class) 告訴JUnit使用PowerMockRunner進(jìn)行測(cè)試。 @PrepareForTest({DemoDao.class}) 所有需要測(cè)試的類列在此處,適用于模擬final類或有final, private, static, native方法的類。 @PowerMockIgnore({"javax.management.", "javax.net.ssl."}) 為了解決使用powermock后,提示classloader錯(cuò)誤。 @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樣例
這是一個(gè)需要被mock的類,里面有私有方法、靜態(tài)方法等等下面就用這個(gè)類來演示各個(gè)方法的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();
// 重點(diǎn)這一句
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é)淺,難免會(huì)有紕漏,假如你發(fā)現(xiàn)了錯(cuò)誤的地方,還望留言給我指出來,我會(huì)對(duì)其加以修正。 如果你覺得文章還不錯(cuò),你的轉(zhuǎn)發(fā)、分享、贊賞、點(diǎn)贊、留言就是對(duì)我最大的鼓勵(lì)。 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取