正在单元测试的对象是一种代码异味吗?例如,假设我有一个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("hellonworld");
}
}
这是合理的做法吗?我觉得测试间谍对象而不是实际类很奇怪,但我真的想不出反对它的理由。
我将分两部分回答这个问题。首先,是的,模拟或监视被测试的类是代码气味。这并不意味着它不能正确完成,而是它容易冒险,应该尽可能避免。
WRT您的具体示例,我将了解如何正确使用间谍,但这将基于您在其他地方完全单元测试getStringMetadata
的断言。这就引出了一个问题,如果你在其他地方getStringMetadata
完全单元测试,那么你必须知道如何测试它,因此为什么不在没有间谍的情况下测试getNumLines
。
话虽如此,millhouse
提出了一个很好的观点,但无论哪种方式,您都必须在某个地方对昂贵的代码进行单元测试。他的建议对帮助隔离昂贵的代码并确保您只需要测试/练习一次大有帮助。
在这种情况下,存根被测方法调用的方法是完全合法的。这甚至是我能想到的单独测试它的唯一方法。您只是不想将单个方法提取到它自己的类中,仅用于测试。
不过要注意存根方法中的副作用。存根返回值可能是不够的,如果存根方法有副作用,那么您也必须存根副作用。在某些副作用非常复杂的情况下,这甚至可能是反对它的理由,但这很可能表明被测类本身的实现中存在代码异味。
要回答你的问题,我发现很容易找到理由,但很难找到反对它的理由。这是我每天使用的技术,它帮助我将我的实现拆分为完全隔离单独测试的小方法,而且我还没有看到它的任何限制。