Mock spy

spy

通过when语句设置过的方法,调用的是模拟方法;而没有通过when语句设置的方法,调用的是真实方法。

如果在mock后不写when和thenReturn去指定,即便是mock调用任何方法,什么也不会做,也看不到什么效果。

划重点的时候来了,本身mock出来的对象是假的,再调用它的方法,一直都在“造假”。

模拟场景
service中有一个写数据到文件的方法

service层
具体代码如下:

package com.rongrong.powermock.spies;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * @description:
 * @author rongrong
 * @version 1.0
 * @date 2019/12/4 22:45
 */
public class FileService {

    /**
     * 写入文件及数据操
     * @param text
     */
    public void writeData(String text){
        BufferedWriter bw = null;
        try {
            bw=new BufferedWriter(new FileWriter(System.getProperty("user.dir")+"/ronngrong.txt"));
            bw.write(text);
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(bw!=null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

为了模拟调用方法后,啥也没没做这个现象,使用之前学过的方法,这里我们不指定返回值(不加when和thenReturn),即人为干预

使用之前学过的方法测试,具体示例代码如下:

    @Test
    public void testFileService(){
        FileService fileService = PowerMockito.mock(FileService.class);
        fileService.writeData("hellow,rongrong!!");
    }

运行结果如下图,并没有新文件生成,更别说写入内容了
在这里插入图片描述

使用powerMock进行测试
采用 spy 的方式 mock一个对象,然后再调用其中的某个方法,它就会根据真实class 的所提供的方法来调用,具体示例代码如下:

 @Test
    public void testFileServiceWithSpy(){
        FileService fileService = PowerMockito.spy(new FileService());
        File file = new File(System.getProperty("user.dir") + "/ronngrong.txt");
        fileService.writeData("hellow,RongRong!!");
        assertTrue(file.exists());
    }

直接运行这个测试用例,你会发现在项目根目录下生成了一个新文件,并且里面写入我们预期设定的内容,运行结果如下图:
在这里插入图片描述

再来一看,最起码我们运行能看到效果,即我知道调用方法后干了些什么!!


PowerMock中@mock和@spy在mock私有方法的区别,使用@spy模拟私有方法进行测试时sonar统计是有覆盖率的

1.、问题描述

PowerMock使用@spy进行模拟私有方法返回值进行测试时,私有方法中的代码总是会被执行

(如果私有方法中依赖环境等因素,测试则难以进行)

例如如下代码段,需要测试callSumXX方法,同时想要模拟私有方法sumXX的返回值

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.spy(new Calculator());
        PowerMockito.when(cal,"sumXX",anyInt(),anyInt()).thenReturn(2);
        assertEquals(2, cal.callSumXX(1, 2));
    }
}

发现私有方法的代码总是会被执行,而预期的是私有方法就不应该执行:
在这里插入图片描述

查看了下,发现@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));

2、使用@mock和@spy两种方式模拟私有方法进行测试的区别

使用@mock模拟私有方法测试代码如下:

@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));
    }
}

注:因为@mock出来的对象可能已经发生了变化,调用的方法都不是真实的,@mock出来的Calculator对应已经不是原来的Calculator,在进行sonar覆盖率统计时统计出来的Calculator类覆盖率为0.00%.

使用@spy模拟私有方法测试代码如下:

@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使用的真实的Calculator对象实例,调用的都是真实的方法,所以通过这种方式进行测试,在进行sonar覆盖率统计时统计出来的Calculator类覆盖率为50%.

通过如上的分析,通过spy的方式可以隔离环境依赖又能统计出sonar覆盖率,解决了一直依赖困扰着的问题。

以上是经验总结,如有问题可以一起探讨。

附:引用的PowerMock版本

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.6.6</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>1.6.6</version>
    <scope>test</scope>
</dependency>

参考文章:
1、https://www.cnblogs.com/longronglang/p/11986572.html
2、https://blog.csdn.net/hefonek/article/details/81194130

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值