我下面有一段代码,其中Employee类使用反射创建# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.mydemo.gcc.GccBootstrapConfiguration
的对象。我想使用PowerMockito模拟这个AppraisalCalculator
对象。
AppraisalCalculator
在这里,即使我模拟了评估计算器对象,它仍然从class AppraisalCalculator {
public int appraisal() {
return 300;
}
}
class Employee {
public int updateSalary() {
// line 1
AppraisalCalculator ac =
AppraisalCalculator.class.getConstructor().newInstance();
return ac.appraisal();
}
}
class TestRunner {
@Test
public void test() {
AppraisalCalulator acMock=PowerMockito.mock(AppraisalCalculator.class);
PowerMockito
.whenNew(AppraisalCalculator.class)
.withNoArguments()
.thenReturn(600);
Employee emp = new Employee();
int actualValue = emp.updateSalary();
int expectedValue=600;
Assert.equals(expectedValue,actualValue);
}
}
调用真正的appraisal()
方法。如果第1行的实际AppraisalCalculator
是使用new运算符而不是AppraisalCalculator
创建的,则此模拟有效。
任何人都可以解释一下,如果使用反射创建了实际的对象,为什么这不起作用?在这种情况下,我该如何模拟该对象?
答案 0 :(得分:0)
首先让我重新表述您的问题以使代码完全正常工作。
@RunWith(PowerMockRunner.class)
@PrepareForTest(Employee.class)
public class TestRunner {
@Test
public void test() throws Exception {
AppraisalCalculator acMock = PowerMockito.mock(AppraisalCalculator.class);
PowerMockito
.whenNew(AppraisalCalculator.class)
.withNoArguments()
.thenReturn(acMock);
when(acMock.appraisal()).thenReturn(600);
Employee emp = new Employee();
int actualValue = emp.updateSalary();
int expectedValue = 600;
assertEquals(expectedValue, actualValue);
}
}
然后,PowerMock的工作方式是PowerMockRunner
将查看需要准备的每个类(此处为Employee
),然后寻找对我们要替换并执行的构造函数的调用它。这是在类加载时完成的。调用构造函数的真实类字节码将替换为返回模拟的字节码。
问题是,如果使用反射,则PowerMock无法通过读取字节码来知道将调用此构造方法。此后才动态知道。所以没有嘲笑。
如果确实需要创建要通过反射模拟的类,那么我实际上将重构一下代码。
在Employee
中,我将添加
protected AppraisalCalculator getCalculator() {
try {
return AppraisalCalculator.class.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
while是一种专用于隔离计算器构造的方法。
只需创建一个子类
@Test
public void testWithChildClass() {
// Note that you don't need PowerMock anymore
AppraisalCalculator acMock = Mockito.mock(AppraisalCalculator.class);
when(acMock.appraisal()).thenReturn(600);
Employee emp = new Employee() {
@Override
protected AppraisalCalculator getCalculator() {
return acMock;
}
};
int actualValue = emp.updateSalary();
int expectedValue = 600;
assertEquals(expectedValue, actualValue);
}
或部分模拟(间谍)
@Test
public void test2() {
// No PowerMock either here
AppraisalCalculator acMock = Mockito.mock(AppraisalCalculator.class);
when(acMock.appraisal()).thenReturn(600);
Employee emp = spy(new Employee());
doReturn(acMock).when(emp).getCalculator();
int actualValue = emp.updateSalary();
int expectedValue = 600;
assertEquals(expectedValue, actualValue);
}