Mockito Spy'ing on the object to unit test

2022-09-03 07:52:26

监视正在进行单元测试的对象是否是代码异味?例如,假设我有一个类,其工作是简单地计算字符串中的行数。--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测试,以测试该方法,同时模拟昂贵的调用。我决定使用Mockito的间谍机制来返回一个虚拟值。getNumLinesgetStringMetadatagetStringMetadata

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

这是一件合理的事情吗?我觉得测试一个间谍对象而不是实际的类很奇怪,但我真的想不出反对它的理由。


答案 1

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

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

话虽如此,所有这些都是一个很好的观点,但无论哪种方式,你都必须在某个地方对昂贵的代码进行单元测试。他的建议有助于隔离昂贵的代码,并确保你只需要测试/练习一次。millhouse


答案 2

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

不过要小心存根方法中的副作用。对返回值进行存根可能还不够,如果 stubbed 方法有副作用,那么您还必须对副作用进行存根。在某些副作用非常复杂的情况下,这甚至可能是反对它的原因,但这很可能是被测类本身的实现中存在代码气味的迹象。

为了回答你的问题,我发现很容易找到它的理由,但很难找到反对它的理由。这是我每天使用的技术,它帮助我将我的实现拆分为小方法,这些方法在完全隔离的情况下单独测试,我还没有看到它的任何限制。


推荐