我需要为以下方法编写一个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。
您认为对此进行单元测试的正确方法是什么?
我个人倾向于更喜欢嘲弄的方法。是否可以说在测试用例中我已经指定了哪个是被测试类的合作者,因为它在某种程度上也更多地属于设计而不是实现?
祝你好运, 亚历
答案 0 :(得分:1)
我认为你的嘲弄方法看起来很好(我同意如果TreeEntry
和你说的一样复杂,你不应该将它们混合到这些测试中(但我希望你做在其他地方测试TreeEntry
))。如果您正在编写这样的大量测试,那么您应该考虑使用一些Builder模式(如果您有耐心的话,可以使用流利的语法),这样您就可以构建模拟,例如像这样:
BuildANew.TreeEntry().WithPreviousEntryName("testName");
或者您可以创建一个ITreeEntry
接口并根据该接口编写树操作代码,并创建一个简单的存根实现以传递到您的测试中,这样您就不需要乱用了模拟设置代码。或者,为了更清晰的方法,创建界面和模拟它。
答案 1 :(得分:1)
我认为您最好的选择是从TreeEntry中提取接口,在测试代码中使用s基本存根实现它,然后对其进行测试。
答案 2 :(得分:0)
如果您只想测试
public String getPreviousName(TreeEntry entry)
方法我会嘲笑它,因为你只是对这个方法进行单元测试。最好保持您的单元测试快速。
但是如果你有兴趣测试整个文件访问和TreeEntry的创建,我会创建它们。
在我的日常工作中,我首先尝试编写单元测试,然后设计函数,因为它有助于设计易于使用/测试的界面。
答案 3 :(得分:0)
我认为模拟没问题。
另一方面:JUnit测试不仅仅是一个简单的测试,它还是驱动器设计。
如果您发现难以编写测试用例,例如构造函数需要复杂的参数,您可能会重新考虑您的设计。
顺便说一下,我认为方法getPreviousName()
不是必需的,getPreviousEntry()
就足够了。