Android单元测试系列(3)-Mock之PowerMock

这篇具有很好参考价值的文章主要介绍了Android单元测试系列(3)-Mock之PowerMock。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、官网

二、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的版本对应关系如下:

Android单元测试系列(3)-Mock之PowerMock

二、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");
    }
}

Android单元测试系列(3)-Mock之PowerMock

三、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));
    }

Android单元测试系列(3)-Mock之PowerMock

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文章来源地址https://www.toymoban.com/news/detail-401506.html

到了这里,关于Android单元测试系列(3)-Mock之PowerMock的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Springboot 如何使用Powermock做单元测试

    一、为什么要使用Mock工具 在做单元测试的时候,我们会发现我们要测试的方法会引用很多外部依赖的对象,比如:(发送邮件,网络通讯,远程服务, 文件系统等等)。 而我们没法控制这些外部依赖的对象,为了解决这个问题,我们就需要用到Mock工具来模拟这些外部依赖的

    2024年02月14日
    浏览(31)
  • java & jacoco & powerMock 单元测试覆盖率为0怎么解决

    我们项目中使用powerMock作为单元测试的mock工具,统计项目测试覆盖率使用jacoco编译的结果上传到sonar,但是jacoco 和 powerMock在运行时runtime加载代码的时候自定义了类加载器,所以就会有冲突,导致测试覆盖率为0。 使用命令 mvn clean verify sonar:sonar上传jacoco编译结果(这里sonar命令

    2023年04月08日
    浏览(32)
  • 基于Junit4+Mockito+PowerMock实现Controller+Service的单元测试

    一 导入的依赖 二 依赖版本 三 controller测试示例代码       controller         controllerTest         测试结果:覆盖率100%         带异常的Controller         带异常提示的ControllerTest         测试结果,覆盖率100%   三 service测试示例代码         service         serviceTest    

    2024年02月14日
    浏览(36)
  • PowerMock使用-Mock私有方法

    本篇文章将说明如何使用 PowerMock 对私有方法进行 Mock 。关于使用 PowerMock 需要引入哪些依赖,请参考PowerMock使用-依赖准备。 被测试类如下所示。 被测试类中有一个公共方法 isTrue() ,在 isTrue() 方法中会调用 MockPrivateMethod 的私有方法 returnTrue() 。测试类如下所示。 Mock 私有方

    2024年02月11日
    浏览(26)
  • PowerMock使用-Mock静态私有方法

    本篇文章将说明如何使用 PowerMock 对静态私有方法进行 Mock 。关于使用 PowerMock 需要引入哪些依赖,请参考PowerMock使用-依赖准备。 被测试类如下所示。 被测试类中有一个静态公共方法 isTrue() ,在 isTrue() 方法中会调用 MockStaticPrivateMethod 的静态私有方法 returnTrue() 。测试程序如

    2024年02月14日
    浏览(23)
  • Service层代码单元测试以及单元测试如何Mock

    接着上一篇文章:单元测试入门篇,本篇文章作为单元测试的进阶篇,主要介绍如何对Springboot Service层代码做单元测试,以及单元测试中涉及外调服务时,如何通过Mock完成测试。 现在项目都流行前后端代码分离,后端使用springboot框架,在service层编写接口代码实现逻辑。假设

    2023年04月08日
    浏览(38)
  • java的单元测试-mock测试

    对于普通的方法,通常采用断言测试。 对于接口,需要使用mockMvc 对于未开发的功能,需要mockBean模拟一个业务bean java自身携带的工具类,也可以用于一些对抛出异常要求不高的业务或者存在全局异常的项目 另外有一个更加简单的写法,以assert开头 曾使用注入方式得到mockM

    2023年04月08日
    浏览(40)
  • 单元测试junit+mock

    单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围,并没有一个明确的标准,“单元”可以是一个方法、类、功能模块或者子系统。 单元测试通常和白盒测试联系到一起 ,如果单从概念上来讲两者是有区别的,不过我们通

    2024年02月08日
    浏览(52)
  • 单元测试与Mock

    作者:一笑钦陈 邮箱:xianqin_chen@163.com 你好,我是一笑钦陈,《零零后程序员成长之路》作者,一线互联网 Java 工程师。很高兴你阅读我的博客,让我们共同成长进步! 提醒:在接下来您对本博客的阅读中,如果遇到一些内容、图稿、代码等中的勘误都可以通过邮件进行反

    2024年02月08日
    浏览(51)
  • Testify Mock 单元测试

    Testify 提供了单测方便的断言能力,这里的断言是将对代码实际返回的断言,代码的实际输出和预期是否一致。下面是 gin-gonic/gin 代码库的单测代码,Testify 还提供了很多其他的方法: 单元测试中也会存在不稳定的代码,我们的入参虽然保持不变,但每次单测的结果可能会发

    2024年02月03日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包