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>