【问题标题】:Should I use mocking in production code?我应该在生产代码中使用模拟吗?
【发布时间】:2009-07-31 09:56:56
【问题描述】:

我有一种情况,我需要mock 一些生产中的代码。这是为了使一部分代码工作在一半功能上。

我必须选择写一个空类(实现接口),或者使用像moq这样的模拟系统。

所以问题是,模拟系统是达到性能,还是破坏了生产代码的某些可读性

更新
示例:

interface IRocketSystem
{
   void LaunchTheRocket();
}

class SimulationRocketSystem:IRocketSystem
{
...
}

class RocketSystem:IRocketSystem
{
...
}

我发现我在生产中有一个 SimulationRocketSystem 类,那么小而且体内没有很多。模拟系统有一行代码( new Mock ().Object )来替换这样的类。

模拟的优点:
项目中的空类更少。

【问题讨论】:

  • 你能分享一些代码,或者给我们更多的细节吗?只有“全功能”还是“半功能”?还是完全可配置的?

标签: mocking moq


【解决方案1】:

听起来像Null Object。出于两个原因,我不相信使用模拟框架。

首先,它会损害可读性。您使用适当命名的空类来实现接口,然后记录该类中的意图。另一方面,如果您使用模拟框架,您的文档需要嵌入到代码中的某个位置。此外,这会让人感到困惑,因为人们倾向于期望在测试代码中使用模拟,而不了解您使用模拟的意图。

其次,您必须考虑如果有人修改界面会发生什么。如果添加一个方法怎么办?它是否应该默认返回 null - 就像某些框架会做的那样?如果方法必须根据接口契约返回一些特定的返回值怎么办。如果你有一个具体的实现,那么编译器至少会在一定程度上保护你。如果您使用模拟框架,您可能会不走运。

【讨论】:

    【解决方案2】:

    模拟对象是您用于测试的东西,因为它可以让您断言它被正确调用。在我看来,您正在寻找的更像是存根或代理对象。

    由于您最终将实现有问题的类,因此让一个模拟框架为您执行 IMO 几乎没有意义。为什么不直接创建类并根据需要实现它。

    【讨论】:

    • 为什么我需要做一些模拟系统做得比我更好的事情?
    • 我的印象是其他对象目前不存在的原因是因为尚未实现必要的类。如果确实如此,我更喜欢代理类,因为这将允许您根据需要连接有问题的对象。 IE。当必要的功能可用时,您的代理可以将调用转发到真实对象。
    • 正确,我正在使用模拟系统,例如生成存根对象的系统,但是有一个问题,如果我可以在生产中做到这一点。
    • 你可以这样做,但你为什么要这样做?既然您最终将实现必要的类,为什么不从实现代理开始。如果您使用模拟框架中的存根,则必须在开始实现实际类时更改代码。
    【解决方案3】:

    空类有什么问题?

    确实会换成真正的类,所以你也可以从真正的类开始。

    我认为将任何类型的模拟代码放在测试之外是一个糟糕的策略。

    【讨论】:

    • +1,我更喜欢做一些额外的工作并编写自己的模拟/存根,而不是仅仅为了便于使用存根而发布假框架。
    • 我有一个完全实现的类,但有时我不需要所有功能(例如示例)
    • “有时我不具备所有功能”不相关。你有课程,你使用它。关键是生产要完全一致,而不是小。在生产环境中使用 mocks 来替换 working 代码是可怕的。
    【解决方案4】:

    你称之为模拟,但看起来你所做的只是适当的关注点分离。

    在 if 语句上使用多态性来控制应该发生的事情是一件好事。

    例如,假设您希望日志记录是可选的。命令式方法是提供boolean isLogging。然后每次检查布尔值,如if (isLogging) { ...logging code... }

    但是,如果您将实际的日志代码分离为另一个对象的关注点,那么您可以将该对象设置为执行您想要的操作的对象。除了具有表示禁用日志记录和实际写入文件的空路由记录器之外,它还允许您提供写入数据库而不是文件的日志记录对象,它允许您向记录器添加功能,例如作为日志文件轮换。

    这只是很好的面向对象编程。

    【讨论】:

    • 是的,但我需要创建一个空类,还是使用使这变得容易的系统
    • 我不会使用 EasyMock 来创建空类。创建空路由类(在其方法中不执行任何操作),然后为启用日志记录的类继承它并覆盖方法以实际执行某些操作。为了获得额外的奖励,您应该使用依赖注入。
    【解决方案5】:

    这可能有点不寻常,所以我会在我的 cmets 等中明确这是必需的功能。否则有人会对测试代码如何投入生产感到非常困惑!

    至于性能,一如既往,您需要对其进行衡量,因为它是如此具体。但是,我会大胆猜测,当您在模拟您未编写的功能时,它可能比您模拟的实现快得多。这是您可能需要对用户进行教育的事情,因为当您提供最终的功能实现时,他们很可能会看到性能下降:-)

    真正的解决方案是为现有接口提供一个虚拟实现,然后再提供真正的实现。

    【讨论】:

      【解决方案6】:

      我真的不知道性能问题。但是恕我直言,您必须非常小心可读性。声明一个或多个接口并以两种不同的方式实现不是更好吗?

      --编辑

      Mocking 可能更容易,并且使用更少的代码行,但它会增加代码的学习曲线。如果一个新人来查看您的代码,他将需要更多时间来理解。就个人而言,我认为这是代码尚未实现的功能。但是如果有另一个接口实现,或者其他一些好的设计决策,我会更快地“得到它”,并且在更改代码时会感觉更安全。

      嗯,这是个人意见。您的代码将使用与预期不同的模式。它类似于“约定优于配置”。如果您不使用约定,则配置它应该会更困难(在您的情况下,记录并明确您在做什么)。

      【讨论】:

      • 实现之一是存根类,但在模拟系统中是一行代码。所以我个人在考虑模拟系统
      • @Avram 编辑了我的答案,给出了更多的论据。它更容易,但恕我直言,它更令人困惑(因此可读性更低)
      • 新人必须学习单元测试。我正在测试很多模拟。
      【解决方案7】:

      我的建议 - 不要将其投入生产。永远不要在生产中放置任何会减慢、破坏或以其他方式导致您失去工作的东西:)

      但比我认为更重要的是您的公司政策是什么以及您的经理的想法是什么?您永远不应该考虑自己做出这样的决定 - 这称为 CYA。

      【讨论】:

      • 好点,但我说的是我只是负责的项目。
      • 没关系 - 该过程是为了防止出现不良更改,无论谁在项目上工作以及谁负责。
      【解决方案8】:

      我必须选择写一个空的类,或者使用像moq这样的模拟系统。

      将其包装在专用的外观/适配器组件中并使用内部类就足够了。如果您的空类需要在系统中传递,那么您就有问题了。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-09
      • 2011-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-22
      相关资源
      最近更新 更多