【问题标题】:When to use a Mocking Framework?何时使用模拟框架?
【发布时间】:2013-10-30 05:16:11
【问题描述】:

所以我正在为我的单元测试使用模拟框架 (Moq),并且想知道什么时候应该使用模拟框架?

以下两种测试的优缺点是什么:

public class Tests
{
    [Fact]
    public void TestWithMock()
    {
        // Arrange
        var repo = new Mock<IRepository>();

        var p = new Mock<Person>();
        p.Setup(x => x.Id).Returns(1);
        p.Setup(x => x.Name).Returns("Joe Blow");
        p.Setup(x => x.AkaNames).Returns(new List<string> { "Joey", "Mugs" });
        p.Setup(x => x.AkaNames.Remove(It.IsAny<string>()));

        // Act
        var service = new Service(repo.Object);
        service.RemoveAkaName(p.Object, "Mugs");

        // Assert
        p.Verify(x => x.AkaNames.Remove("Mugs"), Times.Once());
    }

    [Fact]
    public void TestWithoutMock()
    {
        // Arrange
        var repo = new Mock<IRepository>();

        var p = new Person { Id = 1, Name = "Joe Blow", AkaNames = new List<string> { "Joey", "Mugs" } };

        // Act
        var service = new Service(repo.Object);
        service.RemoveAkaName(p, "Mugs");

        // Assert
        Assert.True(p.AkaNames.Count == 1);
        Assert.True(p.AkaNames[0] == "Joey");
    }
}

【问题讨论】:

  • Mocking 允许你控制所有你没有测试的变量,这样你只测试一段代码。
  • +1 paqogomez,换句话说,模拟允许您隔离被测代码,以便您只测试特定的代码片段。好处是红色或绿色的测试结果表明代码的哪一部分是错误的。您通常会模拟服务,但通常会更多。然而,在这里,人几乎不需要被嘲笑。

标签: c# unit-testing mocking moq


【解决方案1】:

使用模拟对象真正创建一个单元测试——一个假设所有依赖项都正常运行的测试,你想知道的只是SUT(被测系统——一种奇特的方式说你正在测试的课程)有效。

模拟对象有助于“保证”您的依赖项正确运行,因为您创建的这些依赖项的模拟版本会产生您配置的结果。那么问题就变成了,如果你正在测试的一个类在其他一切都“工作”时表现得应该如此。

当您测试具有缓慢依赖关系的对象(如数据库或 Web 服务)时,模拟对象尤其重要。如果您要真正访问数据库或进行真正的 Web 服务调用,您的测试将需要更多的时间来运行。如果只多花几秒钟,这是可以容忍的,但是当您在 continuous integration 服务器上运行数百个测试时,这会非常快地加起来并削弱您的自动化。

这才是真正让模拟对象变得重要的原因——减少构建-测试-部署周期时间。确保您的测试快速运行对于高效的软件开发至关重要。

【讨论】:

    【解决方案2】:

    我使用一些规则来编写单元测试。

    1. 如果我的被测系统 (SUT) 或被测对象具有依赖关系 然后我嘲笑他们。
    2. 如果我测试一个返回结果的方法,那么我 只检查结果。如果依赖项作为方法的参数传递,则应模拟它们。 (见 1)
    3. 如果我测试 'void' 方法,那么验证模拟是测试的最佳选择。

    Martin Fowler Mocks Aren't Stubs 有一篇旧文章。

    在第一个测试中使用 Mock,在第二个测试中使用 Stub。

    我还看到一些导致您提出问题的设计问题。

    如果允许从AkaNames 集合中删除AkaName,则可以使用存根检查人员的状态。如果您将特定方法 void RemoveAkaName(string name) 添加到 Person 类中,则应使用模拟来验证其调用。 RemoveAkaName 的逻辑应该作为 Person 类测试的一部分进行测试。

    我会为product 使用存根,为您的代码使用模拟repository

    【讨论】:

      【解决方案3】:

      mock 框架是用来去除依赖的,所以单元测试会集中在要测试的“单元”上。在你的情况下,这个人看起来像一个简单的实体类,不需要使用 Mocking。

      【讨论】:

        【解决方案4】:

        模拟有很多好处,尤其是在敏捷编程中,其中许多快速发布周期意味着系统结构和真实数据可能不完整。在这种情况下,您可以模拟存储库以模拟生产代码,以便继续在 ui 或服务上工作。这通常与 Ninject 之类的 IoC 机制相辅相成,以简化向真实存储库的切换。您给出的两个示例是相同的,没有任何其他上下文,我会说这是它们之间的选择问题。 moq 中的 fluent api 可能更容易阅读,因为它是一种自我记录。这是我的看法;)

        【讨论】:

          【解决方案5】:

          Mock 用于测试无法单独运行的对象。假设函数 A 依赖于函数 B,为了对函数 A 进行单元测试,我们甚至最终测试函数 B。通过使用 mock,您可以模拟函数 B 的功能,并且测试可以只关注函数 A。

          【讨论】:

            【解决方案6】:

            模拟框架对于模拟被测代码中的集成点很有用。我认为您的具体示例不是模拟框架的良好候选者,因为您已经可以将依赖项(Person)直接注入代码中。在这种情况下,使用模拟框架实际上会使其复杂化。

            一个更好的用例是如果您有一个存储库调用数据库。从单元测试的角度来看,最好模拟 db 调用并返回预定数据。这样做的主要优点是消除了对现有数据的依赖,而且还提高了性能,因为 db 调用会减慢测试速度。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-04-18
              • 1970-01-01
              • 2019-01-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多