【问题标题】:How to mock a object construction that is created using reflection i.e newInstance() method如何模拟使用反射创建的对象构造,即 newInstance() 方法
【发布时间】:2019-05-05 07:55:36
【问题描述】:

我在下面有一段代码,其中 Employee 类使用反射创建 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() 方法。如果第 1 行的实际 AppraisalCalculator 是使用 new Operator 而不是 newInstance() 创建的,则此模拟有效。

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

【问题讨论】:

  • 请下次将实际代码复制到 SO。您的代码中有许多拼写错误和缺失的部分。很难尝试。

标签: mockito easymock powermockito


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2010-09-10
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多