【问题标题】:Mockito/PowerMock when....then not being invoked?Mockito/PowerMock 什么时候......然后没有被调用?
【发布时间】:2014-07-09 18:46:04
【问题描述】:

当我执行以下测试时,返回的是原始对象而不是模拟对象,因此正在执行真正的方法 getLevelCriteriaForLevel(level)(我在调试器中观察到了这一点)。为什么呢?我很确定这昨天已经奏效了,我没有改变任何东西。

我已经试过了

@PrepareForTest({LevelCriteria.class, LevelGenerator.class})

或像我以前那样使用 MockitoJunitRunner,但这也无济于事。 这是代码(generateConcreteLevel是一个私有方法。只有当我从mock传递这个数据时才会抛出预期的异常。否则不会抛出。测试失败是因为没有抛出异常,因为测试没有使用mock对象但真实对象):

 public class LevelGenerator
 {
    public void createLevel(int level)
    {
        generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
    }

    private void generateConcreteLevel(LevelCriterion levelCriterion)
    {
        int entryGroupCount = levelCriterion.getEntryGroupCount();
        int exitGroupCount = levelCriterion.getExitGroupCount();
        int exitsWhileEntries = levelCriterion.getExitsWhileEntries();
        int maxGroupSize = levelCriterion.getMaxGroupSize();
        List<Question> questions = levelCriterion.getQuestions();
        int speed = levelCriterion.getSpeed();
        Range blueItemsCount = levelCriterion.getBlueItemsCount();
        Range brownItemsCount = levelCriterion.getBrownItemsCount();
        Range greenItemsCount = levelCriterion.getGreenItemsCount();
        Range redItemsCount = levelCriterion.getRedItemsCount();
        Range whiteItemsCount = levelCriterion.getWhiteItemsCount();
        Range timespanBetweenGroups = levelCriterion.getTimespanBetweenGroups();
        float fractionOfCarAmountToLeave = levelCriterion.getFractionOfCarAmountToLeave();
        float fractionOfMinimumItemsAmountInCarParkToStartExits = levelCriterion.getFractionOfMinimumItemsAmountInCarParkToStartExits();

        checkItemsFitInEntryGroups(entryGroupCount, maxGroupSize, blueItemsCount, brownItemsCount, greenItemsCount, redItemsCount, whiteItemsCount);
    }

    private void checkItemsFitInEntryGroups(int entryGroupCount, int maxGroupSize, Range... ranges)
    {
        int totalRangeCount = 0;
        for (Range range : ranges)
        {
            totalRangeCount += range.getMaximum();
        }

        if (totalRangeCount > entryGroupCount * maxGroupSize)
        {
            throw new UnsupportedOperationException("Error in level criterion: Not enough entry groups for Items.");
        }
    }
 }

还有测试...

@RunWith(PowerMockRunner.class)
public class LevelGeneratorTest
{
    @Mock
    private LevelCriteria levelCriteriaMock;

    @InjectMocks
    private LevelGenerator levelGenerator;

    @Test(expected=UnsupportedOperationException.class)
    public void tooLessPositionsInEntryGroups()
    {
        LevelCriterion levelCriterion = new LevelCriterion.LevelCriterionBuilder()
                                .withBlueItemsCount(new Range(10, 10))
                                .withEntryGroupCount(3)
                                .withExitGroupCount(3)
                                .withMaxGroupSize(3)
                                .build();

        when(levelCriteriaMock.getLevelCriteriaForLevel(anyInt())).thenReturn(levelCriterion);

        levelGenerator.createLevel(0);
    }
}

顺便说一句:这不是 Eclipse 问题,因为 Maven 构建会产生相同的错误。 我知道我可以直接用 PowerMockito 的 Whitebox 执行私有方法,我将在重构之后做什么,但问题是,为什么 when...then 在这里不起作用。

【问题讨论】:

    标签: junit mockito junit4 powermock


    【解决方案1】:

    假设你的问题是:

    为什么当我期望有模拟的实现时,真正的方法 getLevelCriteriaForLevel(level) 被执行?

    如果您复制它是一个简单的示例(如果需要,请从当前工作区切换),我将尝试证明一切都按预期正常工作。

    我没有你LevelCriterion.LevelCriterionBuilder(),所以我通过实例化一个LevelCriterion(空对象)来简化你的测试。这无关紧要,因为在我的实现中,我总是会返回 UnsupportedOperationException。

    这是我的 LevelGenerator 的样子。 UnsupportedOperationException 尽可能简单 LevelGenerator#generateConcreteLevel(LevelCriterion)。

    如果这样实现:

    public class LevelGenerator
    {
        LevelCriteria levelCriteria;
    
        public void createLevel(int level)
        {
            generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
        }
    
        private void generateConcreteLevel(LevelCriterion levelCriteriaForLevel)
        {
            throw new UnsupportedOperationException("xxx");
        }
    }
    

    测试是绿色的,在LevelGeneratorTest 之上使用@RunWith(MockitoJUnitRunner.class)

    我没有使用@PrepareForTestPowerMockRunner。我有 Mockito 1.9.5 版。

    我确定我没有使用真正的getLevelCriteriaForLevel(level) 实现,因为它看起来像这样:

    public class LevelCriteria {
    
        public LevelCriterion getLevelCriteriaForLevel(int level) {
            throw new IllegalStateException("Unexpected call");
        }
    }
    

    如果执行了真正的 getLevelCriteriaForLevel(int),则测试为红色(因为抛出了 IllegalStateException)。

    而且测试仍然是绿色的......

    这适用于纯 Mockito(1.9.5 版)方法。 您应该删除所有其他模拟库(PowerMock、EasyMock...)

    您确定您对 when(..).thenReturn(..) 的静态调用是 Mockito 吗?

    你试过了吗:Mockito.when(..).thenReturn(..)

    希望对你有帮助。

    PS:我看到你更新了这个问题。这对我来说有点不公平,但我不会再次更新我的答案。 我不需要generateConcreteLevel(..) 的实现细节来说明when(..) 调用正在工作。再次阅读我的答案。在单独的工作区中尝试一下,您会自己看到。

    PS-2:我也在使用 Eclipse... 为什么会有问题? Eclipse 是一个很棒的 IDE。

    【讨论】:

    • 由于LevelCriterion的数据,generateConcreteLevel抛出异常。当实现的真正 LevelCriterion 对象被传递时,它不会抛出异常。这就是问题所在。您的示例总是引发异常。很明显,那时它总是绿色的。
    • 我在 generateConcreteLevel(LevelCriterion) 中需要一些东西,并且我的实现足以证明您在问题中所说的不是真的:“正在执行真正的方法 getLevelCriteriaForLevel(level) (我用调试器观察到)”在我的示例中这是不可能的,因为我的 getLevelCriteriaForLevel(level) 的实际实现会引发 IllegalStateException(如果代码被执行,单元测试将是红色的)。正如我所说,使用 MockitoJUnitRunner。移除对 PowerMock、EasyMock... 的依赖,你会发现它可以工作了。
    • 我更新了问题。结果必须是 10 > 9 => 异常,但使用的是原始 levelCriteria 而不是 mock,所以返回的是真正的 levelCriterion,不会抛出异常。
    • 我已经更新了我的答案......我真的希望你能自己弄清楚。它按照我的描述工作!
    • 我找到了解决方案。看我的回答。
    【解决方案2】:

    我知道出了什么问题。不幸的是,由于我简化了课程,因此您看不到这个问题的真正问题:

    我今天真正实现的 LevelGenerator 得到了一个参数化的构造函数。这就是为什么它昨天有效但不再有效的原因。显然,在不使用标准构造函数时,Mockito 存在模拟注入问题。

    结果:when...then 按预期工作。

    【讨论】:

    • 这并不是您在此处发布的问题的真正答案,而是您现实生活中问题的解决方案。 ;-) 无论如何,我希望我的回答对你有所帮助。
    猜你喜欢
    • 2011-07-26
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多