【问题标题】:Why do I need a mocking framework for my unittests?为什么我的单元测试需要一个模拟框架?
【发布时间】:2008-11-18 21:19:58
【问题描述】:

最近,围绕 .NET 世界中所有不同的模拟框架进行了相当大的宣传。我还没有完全理解他们的伟大之处。编写我自己需要的模拟对象似乎并不难。特别是在 Visual Studio 的帮助下,我可以快速编写一个类来实现我想要模拟的接口(它为我自动生成几乎所有内容),然后为我的测试所需的方法编写一个实现。完毕!为什么要为了节省几行代码而费尽心思去理解一个模拟框架。还是一个模拟框架不仅仅是为了节省代码行数?

【问题讨论】:

  • 只是一个警告......这个问题不是 .NET 特定的。因此,如果您发现自己在这里,请随意阅读。

标签: unit-testing mocking


【解决方案1】:

当我终于掌握了模拟对象的窍门后,我意识到它们对于单元测试至关重要,原因与双盲测试或对照组对于科学试验必不可少:它们隔离了您实际测试的内容。

如果您正在测试一个通过其他接口进行大量交互的类,您不仅可以节省模拟每个接口的代码行,而且您还可以获得执行诸如“抛出如果调用了意外的方法则异常”或“如果这些方法被乱序调用则异常”。您可以使用模拟框架变得非常复杂,尽管我很快承认学习曲线很长,但当您加快速度时,它们将有助于使您的单元测试更加彻底而不会臃肿。

【讨论】:

  • 如何乱序调用方法? IMO 这是一种代码味道。
  • 取决于模拟下类的工作流程。如果某些东西通过已知的生命周期(例如 init()、run()、teardown())保持状态,那么被测类需要尊重该状态。
  • 这不是有点倒退吗?让调用者负责确保其他人的正确状态?需要正确调用顺序的类不应该是确保正确调用/使用它的类吗?通过抛出异常/断言而不是?并且对于这个“order-requiring-class”的测试不应该检查它们是否在以不适当的顺序调用方法时被抛出?在我看来,这将是一种更有效的方法,而不是将正确使用的责任推卸给该“订单要求类”的所有可能用户。
【解决方案2】:

您实际上在问题中确定了模拟框架的关键点之一。您自己编写模拟代码的事实不是开发人员应该关心的。模拟框架以编程方式为您提供接口的实现,而且它们是功能性的(基于您的模拟设置)。

例如,如果您正在测试 ICustomerDAO,并且您想对某个方法进行 14 次不同结果的测试,您会怎么做?手动实现 14 个不同的类?我怀疑有人愿意这样做。

当您不关心类的某些部分是否真正起作用时,Mocks 使您能够定义它们会发生什么,例如在您希望它们时抛出异常、返回零结果并确保您处理它正确,等等...

它们是一个很棒的单元测试工具。

【讨论】:

    【解决方案3】:

    以前的问题可能会有所帮助:
    What is a mock and when should you use it?
    Mockist vs classical TDD

    我发现使用模拟框架可以让我更快地生成测试,并且可以更好地验证我期望在测试中发生的事情实际上是发生。我过去曾自己实施过存根或伪造品。我发现我需要生成特定于我想要的测试的存根,这需要很多时间。我可以使用模拟框架更快地创建相同的测试。好的支持以简单的语法生成假货、存根或模拟。

    需要一段时间才能掌握它,我有一段时间避免使用它,但由于@Chamelaeon 所述的原因,现在不会尝试在没有模拟框架的情况下工作。

    【讨论】:

    • +1 你几乎可以说模拟框架提供了 DSL - DSL 的好处是众所周知的。
    【解决方案4】:

    Roy Osherove 有一个关于 Mock 框架的民意调查,在评论部分,有一个关于是否需要 Mock 框架的讨论(尽管很简短)。

    我个人一直按照您所说的进行手动操作,并且效果很好,但这主要是出于习惯,而不是对模拟框架的普遍看法。

    【讨论】:

      【解决方案5】:

      我当然不认为你需要一个模拟框架。它是一个和其他任何框架一样的框架,最终旨在为您节省一些时间和精力。您还可以执行诸如滚动您自己的常见数据结构(如堆栈和队列)之类的事情,但通常只使用内置于您选择的语言的编译器/IDE 的类库中的那些不是更容易吗?

      我确信使用模拟框架还有其他令人信服的理由,尽管我将把它留给 TDD 和单元测试专家来回答。

      【讨论】:

        【解决方案6】:

        出于同样的原因,您不会尝试在没有 NUnit 的情况下编写单元测试。模拟框架将帮助您验证数百个单元测试的状态和行为。花费 2 周左右的时间来加快速度并真正帮助您专注于需要测试的内容是值得的。

        【讨论】:

          【解决方案7】:

          关于模拟框架让我感到困扰的一件事是“给定 i/p 的函数应该 o/p”通过

          when(mock.someMethod("some arg")).thenReturn("something");

          语句分布在许多单元测试类中。

          让我用一个例子来详细说明。假设有一个 DAO 接口函数 getEmp(int EmpID),它在将员工 ID 作为参数传递时返回一个员工对象。假设这个函数被 10 个不同的单元测试类模拟。现在,如果将来更改此函数以返回更新版本的 Employee 对象,则必须访问 10 个不同的类中的每一个来更新此更改。

          缺点如下……

          a) 我不知道如何找出模拟此函数的所有类,以便我可以去更新此更改。

          b) 我现有的使用模拟 DAO 对象的测试用例仍然幸福地没有意识到 DAO 接口发生的变化,因为模拟没有改变,因此继续保持绿色。 理想情况下,如果我自己编写一个模拟类并在任何地方使用它,我将只有一个地方可以更新 Employee 对象的新版本。此外,一旦我在这个地方更新,我所有使用模拟的现有测试用例都会中断,然后我会确切地知道我需要去哪些地方并为新的员工对象进行更新。

          关于我的观点的任何想法..

          【讨论】:

            【解决方案8】:

            模拟框架的优点之一是它允许对被模拟的对象设置期望。有了期望,我就可以设置各种条件来运行正在测试的代码。

            【讨论】:

              【解决方案9】:

              隔离框架或mocking framework 允许您测试所需的代码,而无需依赖它。它可以进行短期运行测试,允许您快速调试,并轻松地围绕代码构建测试安全网。不同的框架有不同的特性,如前所述——它是一个工具,你应该为工作选择合适的工具。

              【讨论】:

                【解决方案10】:

                我使用 rhino 模拟作为模拟框架。我和其他 5 位开发人员在一个为期 8 个月的大型企业应用程序中使用了它。我们在项目中使用了 tdd。它值得吗?我猜。使用模拟是否有如此巨大的卖点,以至于我必须在每个项目中都使用它?在我看来,没有。这不是必需的,它只是一个工具,如果你想尝试一下就可以使用。有些项目你可以推出自己的模拟类,因为这里有些人说他们更喜欢 - 这更容易。其他项目更大,可能需要一个模拟框架。关键词(在我看来)是可能需要...你需要多少代码覆盖率?对我来说,这是使用模拟的另一个考虑因素。我用 tdd/rhino 模拟做的项目要求我们有 80% 的代码覆盖率,所以模拟帮助我们实现了这一目标。如果我们的代码覆盖率要求较低,例如 40%,我们可能不会使用模拟框架,而是像其他人提到的那样编写自己的模拟类。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-07-23
                  • 1970-01-01
                  相关资源
                  最近更新 更多