【问题标题】:How to Unit Test this (use mocks, or classical unit test?)如何对此进行单元测试(使用模拟或经典单元测试?)
【发布时间】:2011-02-28 22:21:18
【问题描述】:

我需要为以下方法(简化)编写一个 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,也会导致测试失败。

你认为单元测试的正确方法是什么?

我个人更喜欢嘲笑的方式。是否可以说在测试用例中我已经指定了哪些是被测类的协作者,因为这在某种程度上也更多地属于设计而不是实现?

最好的问候, 亚历克斯

【问题讨论】:

    标签: java unit-testing mocking


    【解决方案1】:

    我认为你的模拟方法看起来不错(我同意如果 TreeEntry 像你说的那样复杂,你不应该将它们混入这些测试中(但我希望你测试TreeEntry 其他地方))。如果你正在编写很多这样的测试,你应该考虑使用一些 Builder 模式(如果你有耐心的话,可以使用流畅的语法),这样你就可以构建 mock,例如像这样:

    BuildANew.TreeEntry().WithPreviousEntryName("testName");
    

    或者您可以创建一个ITreeEntry 接口并根据该接口编写您的树操作代码,并创建一个简单的存根实现以传递到您的测试中,这样您就不需要搞乱模拟了设置代码。或者,为了更简洁的方法,创建界面模拟它。

    【讨论】:

      【解决方案2】:

      我认为您最好的选择是从 TreeEntry 中提取一个接口,在您的测试代码中使用基本存根实现它,然后针对它进行测试。

      【讨论】:

        【解决方案3】:

        如果你只想测试

        public String getPreviousName(TreeEntry entry)
        

        method 我会嘲笑它,因为你只是在单元测试这个方法。保持单元测试快速是一个好习惯。

        但如果您有兴趣测试整个文件的访问权限并创建您的 TreeEntry,我会创建它们。

        在我的日常工作中,我尝试先编写单元测试,然后再设计功能,因为它有助于设计易于使用/测试的界面。

        http://en.wikipedia.org/wiki/Test-driven_development

        【讨论】:

          【解决方案4】:

          我认为模拟还可以。

          另一方面:JUnit 测试不仅仅是一个简单的测试,它还驱动设计。

          如果您发现难以编写测试用例,例如构造函数需要复杂的参数,您可能会重新考虑您的设计。

          顺便说一句,我不认为getPreviousName()方法是必要的,getPreviousEntry()就足够了。

          【讨论】:

            猜你喜欢
            • 2012-01-08
            • 1970-01-01
            • 1970-01-01
            • 2019-08-22
            • 2018-04-04
            • 2021-09-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多