Mockito Spy'ing对被单元测试的对象

时间:2013-10-09 00:37:28

标签: java unit-testing junit mockito

窥探正在进行单元测试的物体是否有代码味道?例如,假设我有一个LineCounter类,其作用是简单地计算字符串中的行数。 -

class LineCounter {
    public int getNumLines(String string) {
        String metadata = getStringMetadata(string);

        // count lines in file
        return numLines;
    }

    /** Expensive operation */
    protected String getStringMetadata(String string) {
        // do stuff with string
    }
}

现在我想为此编写一个JUnit 4测试来测试getNumLines方法,同时模拟昂贵的getStringMetadata调用。我决定使用Mockito的间谍机制让getStringMetadata返回虚拟值。

class LineCounterTests {
    @Test public void testGetNumLines() {
        LineCounter lineCounterSpy = Mockito.spy(new LineCounter());

        // Mock out expensive call to return dummy value.            
        Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo");

        assertEquals(2, lineCounterSpy.getNumLines("hello\nworld");
    }
}

这是否合理?我觉得测试一个Spy对象而不是实际的类很奇怪,但我真的不能想到反对它的原因。

2 个答案:

答案 0 :(得分:3)

我将分两部分回答这个问题。首先,是的,它是代码气味来模拟或监视被测试的类。这并不意味着它无法正确完成,而是存在风险,应尽可能避免。

WRT您的具体示例,我会看到间谍是如何被正确使用的,但这可以基于您在其他地方完全单元测试getStringMetadata的断言。这就引出了一个问题,如果你在其他地方进行了完全单元测试getStringMetadata,那么你必须知道如何测试它,以及为什么不在没有间谍的情况下测试getNumLines

所有这一切,millhouse提出了一个很好的观点,但无论哪种方式,你必须在某处对昂贵的代码进行单元测试。他的建议有很长的路要走,以帮助隔离昂贵的代码并确保您只需测试/锻炼一次。

答案 1 :(得分:0)

在这种情况下,存根被测试方法调用的方法是完全合法的。它甚至是我能想到的唯一方法来单独测试它。您只是不想将单个方法提取到它自己的类中,仅用于测试。

请注意存根方法中的副作用。如果存根方法有副作用,那么存储返回值可能还不够,那么你也必须存在副作用。在副作用非常复杂的某些情况下甚至可能是反对它的原因,但这很可能是在测试类本身的实现中代码气味的指示。

要回答您的问题,我发现很容易找到 的原因,但很难找到反对的原因。这是我每天使用的技术,它帮助我将我的实现分解为完全隔离的单独测试的小方法,我还没有看到任何限制。