如何模拟使用反射即newInstance()方法创建的对象构造

时间:2019-05-05 07:55:37

标签: mockito easymock powermockito

我下面有一段代码,其中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创建的,则此模拟有效。

任何人都可以解释一下,如果使用反射创建了实际的对象,为什么这不起作用?在这种情况下,我该如何模拟该对象?

1 个答案:

答案 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);
    }