java – 如何对此进行单元测试(使用模拟或经典单元测试?)

我需要为以下方法编写一个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接口并根据该接口编写树操作代码,并创建一个简单的存根实现以传递到您的测试中,这样您就不需要乱用模拟设置代码.或者,为了更清晰的方法,创建界面并模拟它.

转载注明原文:java – 如何对此进行单元测试(使用模拟或经典单元测试?) - 代码日志