代码之家  ›  专栏  ›  技术社区  ›  Oded

你应该只模仿你自己的类型吗?

  •  36
  • Oded  · 技术社区  · 15 年前

    我读完了 TDD: Only mock types you own 马克·尼达姆的作品,想知道这是否是最佳实践?

    请注意,他不是反对嘲弄,而是直接反对嘲弄——他确实说写一个包装纸就可以了。

    7 回复  |  直到 7 年前
        1
  •  22
  •   akuhn    15 年前

    取决于你是指mock还是mock?…

    假设您只是使用一个模拟框架(如mockito)来创建存根,那么创建您不拥有的类型的存根是完全正确和合理的。

    但是,如果您使用模拟框架(例如mockito)创建mock™对象,那么您最好按照mock™福音传道者的建议进行。就我个人而言,我对这一运动失去了联系,所以我不能告诉你,马克·尼达姆的建议是否应该被视为犹太教徒。

    讽刺的是,马克写的关于嘲笑的东西 EntityManagers 在冬眠中,听起来很合理。但我怀疑我们能否从那个特定的案例中归纳出一条规则,比如“永远不要模仿你不拥有的类型”。有时可能有意义,有时没有。

        2
  •  27
  •   Rogério    14 年前

    我的回答是“不”。在给定的单元测试环境中,您应该模拟任何有意义的东西。不管你是否“拥有”被模仿的类型。

    如今,在Java或.NET环境中,一切(我真的意味着一切)都很容易被嘲笑。因此,没有技术上的理由去麻烦首先编写额外的包装器代码。


    我最近(2010年11月)一直在思考的一些其他想法表明,“只有你自己的模拟类型”是多么不合逻辑:

    1. 假设你 为第三方API创建一个包装器,然后在单元测试中模拟该包装器。不过,稍后您会发现包装器可以在另一个应用程序中重用,因此您将其移动到单独的库中。因此,包装器现在不再由您“拥有”(因为它在多个应用程序中使用,可能由不同的团队维护)。开发人员应该为旧的包装器创建一个新的包装器吗????继续递归地做,一层又一层地添加基本上无用的代码?
    2. 假设有人已经为一些不平凡的API创建了一个很好的包装器,并将其作为可重用库提供。如果所说的包装器正是我的特定用例所需要的,那么我应该首先使用几乎相同的API为包装器创建一个包装器,这样我就可以“拥有”它了吗?!?

    对于一个具体的和现实的例子,考虑Apache CuMon电子邮件API,它只不过是标准Java邮件API的包装器。既然我不拥有它,那么每当我为一个需要发送电子邮件的类编写单元测试时,我应该始终为Commons电子邮件API创建一个包装器吗?

        3
  •  14
  •   H6_    10 年前

    我喜欢 explanation the Mockito project gives 关于这个问题。

    不要模仿你不拥有的类型!

    这不是硬线,但穿过这条线可能 反响!(很可能会)

    1. 想象一下模拟第三方lib的代码。在第三个库的特定升级之后,逻辑可能会发生一些变化,但是测试 套件执行得很好,因为它是模拟的。所以以后, 认为一切都很好,建造的墙毕竟是绿色的, 软件已部署并…繁荣
    2. 这可能是当前设计与第三方库的耦合不够的迹象。
    3. 另一个问题是,第三方自民党可能很复杂,甚至需要大量的模拟才能正常工作。导致过度 指定的测试和复杂的夹具,其本身会影响 简洁易读的目标。或不包含代码的测试 足够了,因为模拟外部系统的复杂性。

    相反,最常见的方法是围绕外部创建包装器 尽管人们应该意识到抽象的风险,但是lib/系统 泄漏,其中太多的低级API、概念或异常 超出包装器的边界。为了验证集成 使用第三方库,编写集成测试,并使它们 尽可能简洁易读。

        4
  •  8
  •   cletus    15 年前

    我本来想说“不”,但我看了一眼博文,就知道他在说什么了。

    他特别谈到在冬眠中嘲笑实体经理。我反对这个。实体管理器应该隐藏在DAO(或类似的)中,DAO应该被嘲笑。测试对EntityManager的单线调用完全是在浪费时间,一旦发生任何更改,它就会中断。

    但是,如果您确实有第三方代码,那么您一定要测试如何与之交互。

        5
  •  1
  •   oberlies Jochen Ehret    7 年前

    嗯,所有权问题无关紧要。

    相关问题是 耦合 ,也就是说,您的测试代码指定了什么。您当然不希望测试代码指定您碰巧使用的某个库的API的详细信息。这就是您在测试类中直接使用mockito模拟库时得到的结果。

    widespread solution proposal 因为这个问题是在库周围创建一个包装器,然后模拟这个包装器。但这有以下缺点:

    • 包装器中的代码没有测试。
    • 包装器可能是一个不完美的抽象,因此包装器的API可能需要更改。如果您在许多测试中模拟了包装器,那么您必须适应所有这些测试。

    因此,我建议 将测试与生产代码中的接口完全分离 . 不要将模拟直接放入测试代码,而是创建一个单独的存根类来实现或模拟生产接口。然后在存根中添加第二个接口,允许测试进行必要的设置或断言。然后,您只需要在生产接口发生更改时调整一个类,甚至可以模拟/存根复杂或频繁更改的库的接口。


    注意:所有这一切都假定使用模拟或存根实际上是必要的。我没有在这里讨论这个问题,因为它不在OP的问题范围内。但真的 问问自己,你是否需要使用模型/存根 . 根据我的经验,它们被过度使用了…

        6
  •  0
  •   AutomatedTester    15 年前

    我同意马克的说法。不幸的是,你不能嘲笑所有的事情,有些事情你不想嘲笑,只是因为你正常使用它是一个黑匣子。

    我的经验法则是模拟的东西,它可以使测试快速进行,但不会使测试变得脆弱。记住并不是所有的假货都是一样的 Mocks are not Stubs .

        7
  •  0
  •   kriss    15 年前

    我当然是少数人,但我认为嘲笑是一种代码味道,如果可能的话,可以使用依赖注入。其基本原理是模拟基本上是测试一些难以测试的代码的变通方法。模拟会削弱测试,因为它们的行为(充其量)就像库的特定版本。如果库发生变化,您的测试将释放所有的检查值。

    你可以从上面看到,我使用的是马克·尼达姆自己的论点,但不是说你不应该嘲笑你不拥有的对象,而是说你根本不应该嘲笑…

    好吧,如果依赖注入不是一个选项,那么让我们模拟一下……但是你必须明白你的测试是假的,不会表现得像生产代码。这不是真正的单元测试,只是部分伪造的。如果可能的话,您可以通过添加检查模拟对象的行为是否如预期的那样的测试来减少这种情况。

    推荐文章