目錄
一、官網(wǎng)
二、Demo示例?
三、PowerMock常用的測試方法
1. Private
1.1 私有變量
1.2 私有方法
2. Final
3. Static
Android單元測試系列(3)-Mock之Mockito_Chris_166的博客-CSDN博客
Android單元測試系列(1)-開篇_Chris_166的博客-CSDN博客
這兩篇中已經(jīng)分別介紹過Mockito的使用和局限性,本篇將介紹PowerMock,用來擴(kuò)展Mockito功能,彌補(bǔ)其局限性(Mockito不能mock private、static方法和類,一些版本不能mock final方法和類),同時(shí)PowerMock還增加了很多反射方法來修改靜態(tài)和非靜態(tài)成員等。
一、官網(wǎng)
PowerMock · GitHub
GitHub - powermock/powermock: PowerMock is a Java framework that allows you to unit test code normally regarded as untestable.
https://github.com/powermock/powermock/wiki/Mockito
PowerMock是依賴Mockito的,所以使用時(shí)要同時(shí)引入,且版本也必須一一對應(yīng)。
PowerMock跟Mockito的版本對應(yīng)關(guān)系如下:
二、Demo示例?
這里Demo按照Mockito 2.8.9,PowerMock 1.7.x來搭配。
// Gradle依賴
dependencies {
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
testImplementation 'org.mockito:mockito-core:2.8.9'
testImplementation "org.powermock:powermock-api-mockito2:1.7.1" // 這個(gè)mockito2必須引入,否則的話會(huì)找不到PowerMockito類
testImplementation "org.powermock:powermock-module-junit4:1.7.1"
testImplementation 'org.powermock:powermock-core:1.7.1'
// testImplementation "org.powermock:powermock-module-junit4-rule:1.7.1"
// testImplementation "org.powermock:powermock-classloading-xstream:1.7.1"
// androidTestImplementation 'androidx.test.ext:junit:1.1.3'
//androidTestImplementation "org.mockito:mockito-android:4.4.0"
}
// 被測試的代碼
package com.fanff.unittestdemo.junitdemo;
public class Calculator {
public int addExact(int x, int y) {
return x + y;
}
public int subtractExact(int x, int y) {
return x - y;
}
public int multiplyExact(int x, int y) {
return x * y;
}
// TODO: zero case
public int intDivide(int x, int y) {
if (y == 0) {
return dealZeroCase();
} else {
return x / y;
}
}
private int dealZeroCase() {
return 0;
}
}
// 測試代碼
需求:測試intDivide()方法中是否有調(diào)用到dealZero()方法
package com.fanff.unittestdemo.mockdemo;
import com.fanff.unittestdemo.junitdemo.Calculator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.mockito.Mockito.times;
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
public class CalculatorMockTest {
@Test
public void testIntDivide() {
Calculator calculator = Mockito.spy(Calculator.class);
calculator.intDivide(1, 0);
}
@Test
public void testPrivateIntDivideMethod() throws Exception {
Calculator calculatorPowermockObj = PowerMockito.spy(new Calculator());
calculatorPowermockObj.intDivide(1, 0);
PowerMockito.verifyPrivate(calculatorPowermockObj, times(1)).invoke("dealZeroCase"); // Pass
}
}
這里說明幾點(diǎn):
1.? @RunWith(PowerMockRunner.class) :這里使用的Runner是PowerMockRunner,這樣就可以與Mokito兼容了,即Mokito里的方法在這里也都是可以正常使用的;
2.?@PrepareForTest({Calculator.class}):務(wù)必記得加上這個(gè)PrepareForTest的注解,否則進(jìn)行verify測試的時(shí)候怎么測試都是pass的,如下沒有寫這個(gè)注解,verifyPrivate()怎么測試都是pass:
@RunWith(PowerMockRunner.class)
// @PrepareForTest({Calculator.class})
public class CalculatorMockTest {
@Test
public void testPrivateIntDivideMethod() throws Exception {
Calculator calculatorPowermockObj = PowerMockito.spy(new Calculator());
calculatorPowermockObj.intDivide(8, 8);
// 如下測試,預(yù)期應(yīng)該是fail的,但是居然實(shí)際測試為pass,因?yàn)闆]有加注解@PrepareForTest({Calculator.class})
PowerMockito.verifyPrivate(calculatorPowermockObj, times(6)).invoke("dealZeroCase");
}
}
三、PowerMock常用的測試方法
主要來看看PowerMock相對Mockito擴(kuò)展的幾點(diǎn):private、final、static.
1. Private
// 被測試的類
package com.fanff.unittestdemo.junitdemo;
import java.util.ArrayList;
import java.util.List;
/**
* 用來做Powermock的測試.
* 方法名/類定義/魔數(shù)都是為了簡單隨意寫的,僅僅只是用來介紹每種mock手段而已
*/
public class Person {
private int mInvaildParam; // 僅僅是用來做私有構(gòu)造方法的說明。私有構(gòu)造方法常用于單例設(shè)計(jì)模式
private String mName;
private final List<String> mAddressList = new ArrayList<>();
private School mInnerSchoolObj;
private Person(int invaildParam) {
mInvaildParam = invaildParam;
System.out.println("Just test, param = " + invaildParam);
}
public Person(String name) {
mName = name;
}
public void modifyName(String name) {
modifyInnerName(name);
}
public String getName() {
return mName;
}
public void addAddressList(String address) {
mAddressList.add(address);
}
public void addInnerAddressList(String innerAddr) {
mAddressList.add(innerAddr);
}
public List<String> getAllAddress() {
return mAddressList;
}
public String getSchoolNo() {
return mInnerSchoolObj.getNo();
}
public int getModifyInfoTimes(String info) {
return getInnerModifyInfoTimes(info) + 2;
}
private int getInnerModifyInfoTimes(String info) {
return 1;
}
private void modifyInnerName(String name) {
mName = name;
}
public static class School {
private String no;// 學(xué)號
public void setNo() {
}
public String getNo() {
return no;
}
}
}
1.1 私有變量
package com.fanff.unittestdemo.mockdemo;
import com.fanff.unittestdemo.junitdemo.Person;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.powermock.api.support.membermodification.MemberModifier;
import org.powermock.reflect.Whitebox;
import java.util.List;
public class PersonTest {
private Person mPerson;
@Before
public void setUp() throws Exception {
mPerson = new Person("Chris");
}
@After
public void tearDown() throws Exception {
mPerson =null;
}
/**
* 訪問私有成員
*/
@Test
public void testGetPrivateMemberValue() {
/ 讀取
String name = Whitebox.getInternalState(mPerson, "mName");
Assert.assertEquals("Chris", name);
mPerson.addAddressList("Wuhan");
mPerson.addAddressList("Shenzhen");
List<String> list = Whitebox.getInternalState(mPerson, "mAddressList");
Assert.assertEquals(2, list.size());
Assert.assertEquals("Shenzhen", list.get(list.size()-1));
/ 讀取
/ 兩種修改私有成員的方法
Whitebox.setInternalState(mPerson, "mName", "FanFF");
Assert.assertEquals("FanFF", mPerson.getName());
try {
MemberModifier.field(Person.class, "mName").set(mPerson, "FanFF_166");
} catch (Exception e) {
}
Assert.assertEquals("FanFF_166", mPerson.getName());
/ 修改
}
}
1.?讀取私有變量使用Whitebox.getInternalState()方法;
2. 修改私有變量,可以有如下兩種方法:
(1)?Whitebox.setInternalState()
(2)?MemberModifier.field()
1.2 私有方法
/**
* 訪問私有成員方法
*/
@Test
public void testPrivateMethod() throws Exception {
/ Verify私有方法 //
Person personMockObj = PowerMockito.mock(Person.class);
personMockObj.getModifyInfoTimes("school");
PowerMockito.verifyPrivate(personMockObj, times(0)).invoke("getInnerModifyInfoTimes", anyString());
Person personSpyObj = PowerMockito.spy(new Person("Chris"));
personSpyObj.getModifyInfoTimes("school");
PowerMockito.verifyPrivate(personSpyObj, times(1)).invoke("getInnerModifyInfoTimes", anyString());
/ Invoke私有方法 /
Whitebox.invokeMethod(mPerson, "addInnerAddressList", "inner_default_addr");
Assert.assertEquals("inner_default_addr", mPerson.getAllAddress().get(0));
// 對私有方法進(jìn)行修改
PowerMockito.replace(PowerMockito.method(Person.class, "modifyInnerName")).with(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Whitebox.setInternalState(proxy, "mName", "modify name, haha");
return null;
}
});
mPerson.modifyName("FanFF_00");
String curName = mPerson.getName();
System.out.println("Name = " + curName);
Assert.assertEquals("modify name, haha", curName);
}
/**
* 調(diào)用私有構(gòu)造方法
*/
@Test
public void testPrivateConstructMethod() throws Exception {
Person personPrivConstruct = Whitebox.invokeConstructor(Person.class, "testPrivate");
personPrivConstruct.addAddressList("private_province");
Assert.assertEquals("private_province", personPrivConstruct.getAllAddress().get(0));
}
1. PowerMockito的Verify需要用@PrepareForTest之前已經(jīng)說過了,用到幾個(gè)類就{}包幾個(gè)類。另外注意配套使用,例如:Mockito.spy()與Mockito.verify??()配套使用,PowerMockito.spy()與PowerMockito.verifyPrivate??()配套使用;
2. 使用PowerMockito.verifyPrivate()驗(yàn)證私有方法;
3. 使用PowerMockito.replace()對私有方法進(jìn)行修改,Whitebox.invokeMethod()調(diào)用私有方法,Whitebox.invokeConstructor()調(diào)用私有構(gòu)造方法
when(), doCallRealMethod()...這些打樁方法也可以用在私有方法里去ignore一些實(shí)現(xiàn)等,這里就不詳細(xì)介紹了。
2. Final
mock對象用PowerMockito.mock()即可,其他的常用方法前面也介紹過。
3. Static
Whitebox.getInternalState()、Whitebox.invokeMethod()、PowerMockito.replace()這些方法的用法都已經(jīng)介紹過了,需要注意的是mock Static對象時(shí)使用PowerMockito.mockStatic(),驗(yàn)證靜態(tài)方法使用PowerMockito.verifyStatic()
PowerMock解決不了匿名內(nèi)部類場景,說明如下:文章來源:http://www.zghlxwxcb.cn/news/detail-401506.html
java - How do I use Powermockito to mock the construction of new objects when testing a method in an anonymous class? - Stack Overflow文章來源地址http://www.zghlxwxcb.cn/news/detail-401506.html
到了這里,關(guān)于Android單元測試系列(3)-Mock之PowerMock的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!