【问题标题】:Partial-mocking considered bad practice? (Mockito)部分模拟被认为是不好的做法? (模仿)
【发布时间】:2012-10-03 15:56:09
【问题描述】:

我正在使用 Mockito 对业务对象进行单元测试。业务对象使用通常从数据库获取数据的 DAO。为了测试业务对象,我意识到使用单独的内存 DAO(将数据保存在 HashMap 中)比编写所有的

when(...).thenReturn(...)

声明。为了创建这样一个 DAO,我首先部分模拟了我的 DAO 接口,如下所示:

when(daoMock.getById(anyInt())).then(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        int id = (Integer) invocation.getArguments()[0];
        return map.get(id);
    }
});

但我突然想到,自己实现一个全新的 DAO 实现(使用内存中的 HashMap)甚至不使用 Mockito(无需从该 InvocationOnMock 对象中获取参数)并制作测试的业务对象更容易使用这个新的 DAO。

此外,我读到部分模拟被认为是不好的做法。我的问题是:就我的情况而言,我正在做的事情是不好的做法吗?有什么缺点?对我来说这似乎没问题,我想知道潜在的问题可能是什么。

【问题讨论】:

    标签: mockito partial-mocks


    【解决方案1】:

    我想知道为什么您需要由HashMap 支持您的假 DAO。我想知道您的测试是否太复杂。我非常喜欢使用非常简单的测试方法,每种方法都可以测试 SUT 行为的一个方面。原则上,这是“每个测试一个断言”,尽管有时我会得到一小部分实际的assertverify 行,例如,如果我断言一个复杂对象的正确性。请阅读http://blog.astrumfutura.com/2009/02/unit-testing-one-test-one-assertion-why-it-works/http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html 了解更多有关此原理的信息。

    因此,对于每种测试方法,您都不应该一遍又一遍地使用您的假 DAO。可能只有一次,最多两次。因此,在我看来,拥有一个充满数据的大 HashMap 要么是多余的,要么表明您的测试做得比它应该做的要多。对于每种测试方法,您实际上应该只需要一两项数据。如果您使用 DAO 接口的 Mockito 模拟设置这些,并将您的 when ... thenReturn 放入测试方法本身,则每个测试都将简单易读,因为特定测试使用的数据将立即可见。

    您可能还想阅读“安排、行动、断言”模式(http://www.arrangeactassert.com/why-and-what-is-arrange-act-assert/http://www.telerik.com/help/justmock/basic-usage-arrange-act-assert.html),并注意在每个测试方法中实现此模式,而不是将其不同部分分散在你的测试类中。

    如果没有看到更多的实际测试代码,很难知道还有什么其他建议可以给你。 Mockito 应该让嘲笑更容易,而不是更难;所以如果你有一个测试没有发生在你身上,那么肯定值得问问你是否在做一些非标准的事情。你所做的不是“部分嘲弄”,但对我来说,这确实像是一种测试气味。尤其是因为它将您的许多测试方法结合在一起 - 问问自己如果您必须更改 HashMap 中的一些数据会发生什么。

    您可能会发现https://softwareengineering.stackexchange.com/questions/158397/do-large-test-methods-indicate-a-code-smell 也很有用。

    【讨论】:

      【解决方案2】:

      在测试我的课程时,我经常使用 Mockito 制作的 mock 和 fake 的组合,这正是您所描述的。在您的情况下,我同意假实现听起来更好。

      部分模拟并没有什么特别的问题,但它使确定何时调用真实对象以及何时调用模拟方法变得有点困难——尤其是因为 Mockito 默默地无法模拟最终方法。对原始类的看似无辜的更改可能会更改部分模拟的实现,从而导致您的测试停止工作。

      如果你有灵活性,我建议你提取一个暴露你需要调用的方法的接口,这样无论你选择mock还是fake都会更容易。

      要编写一个假的,使用一个简单的类(如果你愿意,可以嵌套在你的测试中)实现那个没有 Mockito 的小接口。这将很容易看到正在发生的事情;缺点是如果你写了一个非常复杂的 Fake,你可能会发现你也需要测试 Fake。如果你有很多测试可以使用一个好的 Fake 实现,那么这可能值得额外的代码。

      我强烈推荐 "Mocks aren't Stubs",Martin Fowler 的一篇文章(以他的书重构而闻名)。他回顾了不同类型的测试替身的名称,以及它们之间的区别。

      【讨论】:

      • 谢谢。这对我来说是有用的建议 :) 在我将其标记为已接受之前,我会阅读这篇文章并等待其他答案。
      猜你喜欢
      • 2013-12-08
      • 1970-01-01
      • 1970-01-01
      • 2010-10-22
      • 2011-11-20
      • 1970-01-01
      • 2010-09-26
      • 2014-10-19
      相关资源
      最近更新 更多