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

Mat*_*att 13 java junit unit-testing 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
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想为此编写一个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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这是合理的事吗?我觉得测试一个Spy对象而不是实际的类很奇怪,但是我无法想到反对它的原因.

Joh*_*n B 4

我分两部分回答这个问题。首先,是的,模拟或监视被测类是代码味道。这并不意味着它不能正确完成,而是意味着它容易产生风险,应尽可能避免。

关于您的具体示例,我将了解如何正确使用间谍,但这将基于您在其他地方进行了全面单元测试的断言getStringMetadata。这就引出了一个问题,如果您getStringMetadata在其他地方进行了全面的单元测试,那么您必须知道如何测试它,因此为什么getNumLines不在没有间谍的情况下进行测试。

话虽这么说,millhouse但无论哪种方式,你都必须在某个地方对昂贵的代码进行单元测试。他的建议对于帮助隔离昂贵的代码并确保您只需测试/练习一次有很大帮助。

  • 这是有风险的,因为您可能不是在测试被测类,而是在测试您的模拟/间谍。即使您第一次就做对了,其他人也可能会在您后面对类或测试进行更改,这会无意中导致测试在打算测试生产代码的地方执行间谍代码。 (2认同)