我需要为以下方法编写一个JUnit测试(简化):
/** Return name of previous entry or name of given entry if no previous entry. */
public String getPreviousName(TreeEntry entry) {
if (entry.getPrevious() != null) {
return entry.getPrevious().getName();
} else {
return entry.getName();
}
}
我只是不知道应该怎么测试这个.我也可以
a)使用Mocks模拟参数及其先前的条目或
b)创建一个真正的TreeEntry,它是真正的前一个条目
使用模拟的问题是,我需要在测试用例中说明实际实现需要使用哪种方法,例如:在这里我说正确的名称是通过TreeEntry的getPrevious()然后通过前一个条目的getName()获得的(不考虑if null情况,因为这基本上不是问题):
TreeEntry mockEntry = mock(TreeEntry.class);
TreeEntry mockPreviousEntry = mock(TreeEntry.class);
when(mockEntry.getPrevious()).thenReturn(mockPreviousEntry);
when(mockPreviousEntry.getName()).thenReturn("testName");
assertEquals("testName", classUnderTest.getPreviousName(mockEntry));
但是实现者也可以像这样实现它:
return NameService.getName(entry.getPrevious());
这对测试无关紧要.
但是,如果我使用经典单元测试而没有模拟,我还有另一个问题. TreeEntry的构造函数非常复杂.它有5个参数需要有效的对象.所以创建类似的东西非常复杂:
TreeEntry entry = new TreeEntry(...);
TreeEntry previousEntry = new TreeEntry(...);
entry.setPrevious(previousEntry);
previousEntry.setName("testName");
assertEquals("testName", classUnderTest.getPreviousName(entry));
代码会比这长得多,因为构造函数需要复杂的参数.另一件事是构造函数访问文件系统以从那里加载它的内容,因此它增加了使单元测试更慢.
使用经典方法,它还将测试与TreeEntry的实现联系起来.因为如果在TreeEntry中使用构造函数或前一个条目的设置更改了某些内容,则还需要在测试中更改它.如果它被遗忘,它将导致测试失败,即使是严格的测试类也不是TreeEntry.
你会说什么是单元测试的正确方法?
我个人倾向于更喜欢嘲弄的方法.是否可以说在测试用例中我已经指定了哪个是被测试类的合作者,因为这在某种程度上也更多地属于设计而不是实现?
最好的祝福,
亚历克斯
最佳答案 我认为你的模拟方法看起来很好(我同意如果TreeEntry像你说的那样复杂,你不应该将它们混合到这些测试中(但我希望你在其他地方测试TreeEntry)).如果您正在编写这样的大量测试,那么您应该考虑使用一些Builder模式(如果您有耐心的话,可以使用流利的语法),以便您可以构建模拟,例如像这样:
BuildANew.TreeEntry().WithPreviousEntryName("testName");
或者您可以创建一个ITreeEntry接口并根据该接口编写树操作代码,并创建一个简单的存根实现以传递到您的测试中,这样您就不需要乱用模拟设置代码.或者,为了更清晰的方法,创建界面并模拟它.