我在下面有一个代码,其中员工班级使用反射创建AppraisalCalculator
的对象。我想使用PowerMockito模拟此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);
}
}
在这里,即使我嘲笑了评估计算器对象,它仍然调用AppraisalCalculator
的真实appraisal()
方法。如果使用新操作员而不是newInstance()
创建了第1行的实际AppraisalCalculator
,则此模拟有效。
有人可以解释为什么使用反射创建实际对象,为什么这不起作用?在这种情况下,我该怎么做才能模拟此对象?
让我首先首先重新提出问题将完全工作代码。
@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);
}
}
是一种用于隔离计算器结构的方法。
,只需创建一个孩子班
@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);
}