mockito - 如何模拟使用反射创建的对象构造,即 newInstance() 方法
问题描述
我在下面有一段代码,其中 Employee 类创建了AppraisalCalculator
使用反射的对象。我想AppraisalCalculator
使用 PowerMockito 模拟这个对象。
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 计算器对象,它仍然appraisal()
从AppraisalCalculator
. 如果第AppraisalCalculator
1 行的实际值是使用 new Operator 而不是创建的,newInstance()
则此模拟有效。
如果实际对象是使用反射创建的,谁能解释为什么这不起作用?在这种情况下,我能做些什么来模拟这个对象?
解决方案
让我首先重新表述您的问题将完全有效的代码。
@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);
}
推荐阅读
- python - 在 Google Colaboratory for Python 上安装和导入 MDAnalysis?问题
- api - Vimeo Player 在 Oembed Get 调用时出现 404 错误
- sql - Snowflake - 单独运行查询
- graphql - 多租户 GraphQL 服务器
- php - WooCommerce:在“add_fee 功能”标签中允许 HTML 标签
- javascript - 如何调用特定的谷歌云函数?
- swift - 斯威夫特 | 后台线程中的网络调用立即超时
- deep-learning - ValueError:检查输入时出错:预期dense_21_input具有形状(3027,)但得到的数组具有形状(3072,)
- terminal - 如何从远程代码空间下载文件 (CodeSync)
- php - 如何将上传到 Firebase 存储的文件设置为可通过浏览器按钮下载?