首页 > 解决方案 > 如何模拟使用反射创建的对象构造,即 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. 如果第AppraisalCalculator1 行的实际值是使用 new Operator 而不是创建的,newInstance()则此模拟有效。

如果实际对象是使用反射创建的,谁能解释为什么这不起作用?在这种情况下,我能做些什么来模拟这个对象?

标签: mockitoeasymockpowermockito

解决方案


让我首先重新表述您的问题将完全有效的代码。

@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);
    }

推荐阅读