android单元测试系列-mock之powermock(代码片段)

Chris_166 Chris_166     2022-12-05     402

关键词:

目录

一、官网

二、Demo示例 

三、PowerMock常用的测试方法

1. Private

1.1 私有变量

1.2 私有方法

2. Final

3. Static


Android单元测试系列(3)-Mock之Mockito_Chris_166的博客-CSDN博客

Android单元测试系列(1)-开篇_Chris_166的博客-CSDN博客

这两篇中已经分别介绍过Mockito的使用和局限性,本篇将介绍PowerMock,用来扩展Mockito功能,弥补其局限性(Mockito不能mock private、static方法和类,一些版本不能mock final方法和类),同时PowerMock还增加了很多反射方法来修改静态和非静态成员等。

一、官网

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的,所以使用时要同时引入,且版本也必须一一对应。

PowerMock跟Mockito的版本对应关系如下:

二、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" // 这个mockito2必须引入,否则的话会找不到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()方法中是否有调用到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
    

这里说明几点:

1.  @RunWith(PowerMockRunner.class) :这里使用的Runner是PowerMockRunner,这样就可以与Mokito兼容了,即Mokito里的方法在这里也都是可以正常使用的;

2. @PrepareForTest(Calculator.class):务必记得加上这个PrepareForTest的注解,否则进行verify测试的时候怎么测试都是pass的,如下没有写这个注解,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);
        // 如下测试,预期应该是fail的,但是居然实际测试为pass,因为没有加注解@PrepareForTest(Calculator.class)
        PowerMockito.verifyPrivate(calculatorPowermockObj, times(6)).invoke("dealZeroCase");
    

三、PowerMock常用的测试方法

主要来看看PowerMock相对Mockito扩展的几点:private、final、static.

1. Private

// 被测试的类

package com.fanff.unittestdemo.junitdemo;

import java.util.ArrayList;
import java.util.List;

/**
 * 用来做Powermock的测试.
 * 方法名/类定义/魔数都是为了简单随意写的,仅仅只是用来介绍每种mock手段而已
 */
public class Person 
    private int mInvaildParam; // 仅仅是用来做私有构造方法的说明。私有构造方法常用于单例设计模式
    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;// 学号
        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));

        // 对私有方法进行修改
        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);
    

    /**
     * 调用私有构造方法
     */
    @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之前已经说过了,用到几个类就包几个类。另外注意配套使用,例如:Mockito.spy()与Mockito.verify​()配套使用,PowerMockito.spy()与PowerMockito.verifyPrivate​()配套使用;
2. 使用PowerMockito.verifyPrivate()验证私有方法;
3. 使用PowerMockito.replace()对私有方法进行修改,Whitebox.invokeMethod()调用私有方法,Whitebox.invokeConstructor()调用私有构造方法

when(), doCallRealMethod()...这些打桩方法也可以用在私有方法里去ignore一些实现等,这里就不详细介绍了。

2. Final

mock对象用PowerMockito.mock()即可,其他的常用方法前面也介绍过。

3. Static

Whitebox.getInternalState()、Whitebox.invokeMethod()、PowerMockito.replace()这些方法的用法都已经介绍过了,需要注意的是mock Static对象时使用PowerMockito.mockStatic(),验证静态方法使用PowerMockito.verifyStatic()

PowerMock解决不了匿名内部类场景,说明如下:

java - How do I use Powermockito to mock the construction of new objects when testing a method in an anonymous class? - Stack Overflow

android单元测试系列-mock之mockito(代码片段)

...;这个时候就需要mock这些对象来解决了。二、Demo示例参考AndroidDeveloper中提到的示例来说明Mockito用法。1.目录结构2.被测试的类//gradle引入dependencies  //test目录  testImplementation'org.mockito:mockito-core:4.4.0'  //androidTest目录  //an... 查看详情

补习系列-springboot单元测试之道(代码片段)

目录目标一、About单元测试二、AboutJunit三、SpringBoot-单元测试项目依赖测试样例四、Mock测试五、最后目标了解单元测试的背景了解如何利用springboot实现接口的测试了解如何利用mokito做代码的mock一、About单元测试单元测试其实是... 查看详情

单元测试之mock(moq)(代码片段)

...ock翻译为“嘲弄”,其实就是伪造一个对象用于测试。在单元测试中,被测试方法依赖于其他对象时,为了测试简单一般“伪造”一个这个对象;这样做的目的:不用考虑依赖对象的复杂性(方便准备测试数据)只专注测试被测... 查看详情

单元测试之stub和mock

单元测试之Stub和MockFROM:http://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,记录Log,文件系统之类的)。而我们没法... 查看详情

重学springboot系列之mockito测试(代码片段)

...在做系统的自动化持续集成的时候,会要求自动的做单元测试,只有所有的单元测试都跑通了,才能打包构建。比如:使用maven在打包之前将所有的测试用例执行一遍。这里重点是自动化,所以postman这种工具... 查看详情

android单元测试之mockito的使用

...某些行为。举个栗子参数为aynInt()参数包含某个string参考Android单元测试(二):Mockito框架的使用 查看详情

android单元测试系列-开篇(代码片段)

...目录2.单元测试代码规范3.单元测试验证标准​本篇进行Android单元测试简介。 一、测试金字塔Google官网介绍如下:https://developer.android.com/training/testing/fundamentals1.Smalltests是单元测试,在localJVM就可以运行。2.Mediumtests是集... 查看详情

前端测试框架jest系列教程--mockfunctions

写在前面:  在写单元测试的时候有一个最重要的步骤就是Mock,我们通常会根据接口来Mock接口的实现,比如你要测试某个class中的某个方法,而这个方法又依赖了外部的一些接口的实现,从单元测试的角度来说我只关心我测... 查看详情

mockito-junit+mockito单元测试之打桩when().thenreturn()(代码片段)

什么是Mock测试Mock测试就是在测试过程中,对于某些不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者不容易获取的对象(如JDBC中的ResultSet对象,JPA的CRUDRepository,需要执行数据库操作的)࿰... 查看详情

android单元测试学习总结(代码片段)

文章目录一、本地单元测试1.创建测试类2.Assert类中的常用断言方法3.运行测试类4.运行单个测试方法或多个测试类二、Mockito测试框架的使用1.Mock概念的理解2.Mockito中几种Mock对象的方式3.验证行为verify(Tmock)函数的使用使用`when(Tmeth... 查看详情

spring学习12-spring利用mock进行单元测试

...盼望拥有一组能够对组件开发提供全面测试功能的好用的单元测试。一直以来,与测试独立的Java对象相比,测试传统型J2EEWeb组件是一项更为困难的任务,因为Web组件必须运行在某种服务器平台上并且它们还要与基于HTTP的Web交互... 查看详情

《爆肝整理》保姆级系列教程-玩转charles抓包神器教程(13)-charles如何进行mock和接口测试(代码片段)

...的对象,创建一个Mock对象来模拟对象的行为。Mock方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开。3.Mock测试的优点3.1团队并行工... 查看详情

mock单元测试

  单元测试的思路是在不涉及依赖的情况下测试代码,一般是测试service层的方法,但实际的代码常会涉及到依赖,一个可行的方法就是使用模拟对象来替换依赖对象。1.使用Mockito生成mock对象  Mockito 是一个流行mock框架... 查看详情

单元测试之道读后感

  最近看了一下郑老师发的《单元测之道》这本书,读了之后我对CRRRECT边界条件,还有使用Mock对象,都有了初步的了解对单元测试有了很深刻的认识。这本书从什么是单元测试,为什么要使用单元测试这样的问题人手,以讨... 查看详情

单元测试---mock(代码片段)

mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法.1usingMoq;23//Assumptions:45publicinterfaceIFoo67BarBarget;set;8stringNameget;set;9intValueget;set;10boolDoSomething(stringvalue);11boolD 查看详情

前端抢饭碗系列之vue项目中如何做单元测试建议收藏

 异步代码  我们项目中经常也会涉及到异步代码,比如setTimeout、接口请求等都会涉及到异步,那么这些异步代码怎么来进行测试呢?假设我们有一个异步获取数据的函数fetchData:  exportfunctionfetchData(cb)  ... 查看详情

快速入门java单元测试——mock(代码片段)

背景为了确保代码的质量,对编写的代码进行单元测试是非常有必要的。在JAVA项目中,一般的项目结构比较复杂、依赖众多。在微服务与springboot大行其道的今天,单纯靠junit来进行单元测试一般很难完成对模块的单... 查看详情

快速入门java单元测试——mock(代码片段)

背景为了确保代码的质量,对编写的代码进行单元测试是非常有必要的。在JAVA项目中,一般的项目结构比较复杂、依赖众多。在微服务与springboot大行其道的今天,单纯靠junit来进行单元测试一般很难完成对模块的单... 查看详情