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