PowerMock 单元测试 常见问题

1、引入的包

powermock-module-junit4
powermock-api-mockito2
javassist
mockito-core
2、常用的方法
2.1 方法名上注解

@RunWith(PowerMockRunner.class)
@PrepareForTest(Log.class)
//  @PowerMockIgnore({"javax.management.*", "javax.script.*"})  不要忽略报错
@SuppressStaticInitializationFor({   // 应该抑制 mock的对象静态属性和静态块的初始化
        "com.sleepycat.je.SecondaryDatabase"})

2.2 mock类

PowerMockito.mockStatic(Log.class);
// mockstaic 静态类的方法 如果你doNothing 就不需要mock,默认就是 doNothing
// PowerMockito.doNothing().when(Log.class, PowerMockito.method(Log.class, "e", Exception.class)).withArguments(Mockito.any());
// 被测试类 正常属性值,可以直接set
queryPropertiesconfig = PowerMockito.mock(QueryPropertiesconfig.class);
testOwner.queryPropertiesQconfig = queryPropertiesQconfig;

2.3 mock 私有属性 https://blog.csdn.net/github_32521685/article/details/50556243

// BrandSetParam  mock实例
MemberModifier.field(BrandSetParam.class, "brandSet").set(param, brandSet);
其他的 mock  静态私有 属性方法
PowerMockito.mockStatic(QueryUtils.class);
// Whitebox.setInternalState(QueryUtils.class, "brandsetcount", 10);
Field field = PowerMockito.field(QueryUtils.class, "brandsetcount");
field.setInt(null ,10);

2.4 Mock 私有方法(private method),返回特定值

MemberModifier.stub(MemberMatcher.method(PrivateObject .class, "getPrivateString")).toReturn( "Power Mock");

另外一种获取Method的方法

Method methodToEntity = PowerMockito.method(GetNewStoreProductService.class,
                "toEntity",
                String.class);
        MemberModifier.stub(methodToEntity).toReturn(new ProductEntry() {{
            setProductId(1);
        }});

2.5 抑制父类的方法 和 替换父类的方法

作者:zyxing
链接:https://www.zhihu.com/question/58768289/answer/522121438
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

@RunWith(PowerMockRunner.class)
@PrepareForTest(Bar.class)
public class FooTest {

    @Test
    public void testBar() throws Exception {
        Method method = PowerMockito.method(Bar.class, "bar");
        // 抑制父类的这个方法执行
        PowerMockito.suppress(method);

        Foo foo = new Foo();
        foo.bar();
    }

    @Test
    public void testNumBar() throws Exception {
        Method methodFoo = PowerMockito.method(Foo.class, "numBar");
        // 替换掉父类的方法
        PowerMockito.replace(methodFoo).with(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return 1;
            }
        });

        Foo foo = new Foo();
        int numBar = foo.numBar();

        Assert.assertEquals(1, numBar);
    }    
}

2.6 测试被测对象的私有方法,使用反射

Method method = PowerMockito.method(TrafficInfoWrapperImpl.class, "getTrafficInfoEntity", HotelInfoType.class, TrafficInfoParameter.class);
TrafficInfoEntity positionInfo = (TrafficInfoEntity) method.invoke(trafficInfoWrappers, hotelInfoType, trafficInfoParameter);

2.7 mock被测试方法内部调用的方法
@spy和@mock的对象在使用when…thenReturn有区别:

@mock写法: cal中所有的方法都不是真实的且默认返回null,用mock去模拟返回值时sumXX方法会先执行一次,而因为执行的不是真实的方法所以并没有什么影响。

Calculator cal=PowerMockito.mock(Calculator.class);

PowerMockito.when(cal,“sumXX”,anyInt(),anyInt()).thenReturn(2);

assertEquals(2, cal.callSumXX(1, 2));

@spy写法: cal中所有的方法都是真实的,用when…thenReturn时就会去执行真实的私有方法,那么私有方法里面所有的代码都会执行一遍,这样是不可行的,因为很有可能私有方法就会依赖真实的环境。需要改用doReturn…when才会不执行真实的方法。

Calculator cal=PowerMockito.spy(new Calculator());

PowerMockito.doReturn(2).when(cal,“sumXX”,anyInt(),anyInt());

assertEquals(2, cal.callSumXX(1, 2));

public class Calculator {
    private int sumXX(int a, int b) {
        return a + b;
    }
 
    public int callSumXX(int a, int b) {
        return sumXX(a, b);
    }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
public class CalculatorTest {
    @Test
    public void testSumXX() throws Exception {
        Calculator cal = PowerMockito.mock(Calculator.class);
        PowerMockito.when(cal,"sumXX",anyInt(),anyInt()).thenReturn(2);
        //指明callSumXX调用真实的方法
        PowerMockito.when(cal.callSumXX(anyInt(),anyInt())).thenCallRealMethod();
        assertEquals(2, cal.callSumXX(1, 2));
    }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
public class CalculatorTest {
    @Test
    public void testSumXX() throws Exception {
        Calculator cal = PowerMockito.spy(new Calculator());
        PowerMockito.doReturn(2).when(cal,"sumXX",anyInt(),anyInt());
        assertEquals(2, cal.callSumXX(1, 2));
    }
}

还有一个问题 如果@spy mock的方法,是private 的会执行一次方法内部,public的方法不会执行,直接doReturn 返回值。如下,

SlaveRoomNameCollecter spy = Mockito.spy(underTest);
PowerMockito.doReturn("name").when(spy, "getNameByID", anyInt(), anyString());

2.8 被mock对象 调用自己真实的方法

Mockito.doCallRealMethod().when(operateDistrict).getAllByPrimaryKey(Mockito.any());

3、遇见的异常

java.lang.IllegalStateException: Failed to transform class with name XXXXXX
InterfaceMethodrefInfo cannot be cast to MethodrefInfo
解决办法:

<dependency>
   <groupId>org.powermock</groupId>
   <artifactId>powermock-module-junit4</artifactId>
   <scope>test</scope>
   <exclusions>
      <exclusion>
         <artifactId>javassist</artifactId>
         <groupId>org.javassist</groupId>
      </exclusion>
   </exclusions>
</dependency>

<dependency>
   <groupId>org.javassist</groupId>
   <artifactId>javassist</artifactId>
   <version>3.22.0-GA</version>
</dependency>

参考 : http://www.javaear.com/question/31189086.html

3.2 Mock在前,new 被测试的类的实例 放在后后面,否则可能出现 new 的时候就依赖了 mock的数据,比如有 静态代码块
3.3 mock 任意值 和 特定值 混搭

// 被 mock方法
public static Integer getDefaultInt(String key,Integer defaultValue) {//}
// correct
PowerMockito.when(SystemInfo.getDefaultInt(Mockito.anyString(), Mockito.anyInt())).thenReturn(3); 
// correct
PowerMockito.when(SystemInfo.getDefaultInt("sort.change.day", 2)).thenReturn(3);
// incorrect 不能任意值 和 特定值 混搭
PowerMockito.when(SystemInfo.getDefaultInt(Mockito.anyString(), 2)).thenReturn(3);

对应的报错信息

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.ctrip.hotel.search.das.mlstoreproduct.datafilter.MlstoreproductDataFilterTest.testParseDasEntity(MlstoreproductDataFilterTest.java:57)

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

4、InjectMocks 和 Mock

@InjectMocks:创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。

@Mock:对函数的调用均执行mock(即虚假函数),不执行真正部分。

@Spy:对函数的调用均执行真正部分。

Mockito中的Mock和Spy都可用于拦截那些尚未实现或不期望被真实调用的对象和方法,并为其设置自定义行为。二者的区别在于Mock不真实调用,Spy会真实调用。

    @InjectMocks
    private HotFeatureRepositoryImpl repository;

    @Mock
    private GetProvinceFilterClient<List<HotSearchKeywordStatModel>> getProvinceFilterClient;
    @Test
    public void getHotFeaturesByProvinceAsync_Invalid() {
        repository.getHotFeaturesByProvinceAsync(0);
    }

注意事项:
InjectMocks 被测试类如果有父类,并且会有一些静态属性初始化的话,会在@before前执行,所以可能会出现NPE等错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值