【问题标题】:What are strict and non-strict mocks?什么是严格和非严格模拟?
【发布时间】:2011-03-09 05:25:59
【问题描述】:

我已经开始使用 moq 进行模拟。有人可以解释一下严格和非严格模拟的概念吗?它们如何在最小起订量中使用?

编辑: 我们在哪种场景下使用哪种类型的 mock?

【问题讨论】:

标签: unit-testing mocking moq


【解决方案1】:

我不确定最小起订量,但这里是严格模拟在 Rhino 中的工作方式。我声明我希望在我的对象foo 上调用foo.Bar

foo.Expect(f => f.Bar()).Returns(5);

如果调用代码有

foo.Bar();

那我很好,因为完全符合期望。

但是,如果调用代码是:

foo.Quux(12);
foo.Bar();

然后我的期望失败了,因为我没有明确期望调用foo.Quux

总而言之,如果与预期有任何不同,严格的模拟将立即失败。另一方面,非严格的模拟(或存根)会很乐意“忽略”对foo.Quux 的调用,它应该为foo.Quux 的返回类型T 返回一个default(T)

Rhino recommends that you avoid strict mocks 的创建者(并且更喜欢存根),因为您通常不希望您的测试在收到上述意外调用时失败。当您必须修复数十个依赖于确切原始行为的测试时,重构代码变得更加困难。

【讨论】:

  • 你在哪里指定它是一个严格的模拟?
  • @Sandbox:您可以在the Mock constructor that takes a MockBehavior argument 中指定严格或非严格。默认行为(未指定 MockBehavior 时)似乎是非严格的(他们称之为“松散”)。
  • 我认为在答案本身中包含马克的评论会很好;既解释了如何创建严格与非严格,也提到了别名“松散”。
【解决方案2】:

曾经遇到过Given / When / Then?

  • 给定上下文
  • 当我执行一些事件时
  • 那么结果应该会出现

这种模式出现在 BDD 的场景中,也与单元测试相关。

如果您要设置上下文,您将使用该上下文提供的信息。例如,如果您通过 Id 查找某些内容,那就是上下文。如果它不存在,则测试将不会运行。在这种情况下,您想使用 NiceMock 或 Stub 或其他任何东西 - Moq 的默认运行方式。

如果您想验证结果,可以使用 Moq 的验证。在这种情况下,您想要记录相关的交互。幸运的是,这也是 Moq 默认的运行方式。如果发生您对该测试不感兴趣的事情,它不会抱怨。

StrictMock 可用于当您不希望发生意外交互时。这就是旧式模拟框架的运行方式。如果你在做 BDD 风格的例子,你可能不会想要这个。与将您感兴趣的行为的各个方面分开相比,它倾向于使测试变得有点脆弱和难以阅读。您必须为上下文和结果设置预期,对于将发生的所有结果,无论他们是否感兴趣。

例如,如果您正在测试控制器并模拟您的验证器和存储库,并且您想验证您是否已保存您的对象,那么使用严格模拟您还必须验证您是否已验证首先是对象。我更喜欢在单独的示例中查看行为的这两个方面,因为它使我更容易理解控制器的价值和行为。

在过去的四年里,我没有找到一个需要使用严格模拟的示例——要么是我想要验证的结果(即使我验证了它被调用的次数),要么是用于如果我对提供的信息做出正确回应,我可以判断。所以回答你的问题:

  • 非严格模拟:通常
  • 严格模拟:最好从不

注意:我对 BDD 有强烈的偏见,所以铁杆 TDD 人员可能不同意我的观点,这对他们的工作方式是正确的。

【讨论】:

  • 您如何避免方法更改功能的问题,然后您没有任何测试验证此新功能,因为您的所有单元测试仍然通过?
  • @DanielLorenz 如果要更改功能,请先编写失败的测试;无论您使用哪种模拟,这都是 TDD 循环的核心部分。如果您指的是无意更改的方法,那么仔细考虑责任的可读测试会降低发生这种情况的可能性。
  • 我认为解决这个问题的最佳方法是放松所有测试,除了严格的测试。这样一来,如果您进行大量这样的更改,1 次测试将失败,并会提醒您确保已涵盖所有新案例,但随后修复该测试不会那么痛苦。跨度>
  • @DanielLorenz 如果你做的 TDD 是正确的,它几乎不会捕获错误。然而,它确实有助于出色的设计和自我注释代码。我发现那些认为它是“测试”的人倾向于固定他们的代码,这样它就不会被破坏。我更喜欢让改变变得容易和安全。但是我真的是一个 BDDer,而不是一个 TDDer,即使在这个级别。 dannorth.net/introducing-bdd
  • 我也不是真正的 TDDer,但如果它通过添加 1 个严格测试来防止 1 个错误,那已经是值得的了。 :) 如果您没有任何严格的测试,您可能在没有意识到的情况下错过了单元测试。我宁愿放心,至少有 1 项测试会帮我解决这个问题。
【解决方案3】:

这是一个很好的article
我通常最终会遇到这样的事情

public class TestThis {

    private final Collaborator1 collaborator1;
    private final Collaborator2 collaborator2;
    private final Collaborator2 collaborator3;

    TestThis(Collaborator1 collaborator1, Collaborator2 collaborator2, Collaborator3 collaborator3) {
        this.collaborator1 = collaborator1;
        this.collaborator2 = collaborator2;
        this.collaborator3 = collaborator3;
    }

    public Login login(String username) {
        User user = collaborator1.getUser(username);
        collaborator2.notify(user);
        return collaborator3.login(user);
    }

}

...我为 3 个协作者使用 Strict mocks 来测试登录名(用户名)。我不明白如何永远不要使用 Strict Mocks。

【讨论】:

    【解决方案4】:

    我有一个简单的约定:

    1. 当被测系统 (SUT) 将调用委托给底层模拟层时,使用严格模拟,而没有真正修改或将任何业务逻辑应用于传递给自身的参数。

    2. 当 SUT 将业务逻辑应用于传递给自身的参数并将一些派生/修改的值传递给模拟层时,请使用松散模拟。

    例如: 假设我们有数据库提供者 StudentDAL,它有两种方法:

    数据访问接口如下所示:

    public Student GetStudentById(int id);
    public IList<Student> GetStudents(int ageFilter, int classId);
    

    使用此 DAL 的实现如下所示:

    public Student FindStudent(int id)
    {
       //StudentDAL dependency injected
       return StudentDAL.GetStudentById(id);
       //Use strict mock to test this
    }
    public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest)
    {
      //StudentDAL dependency injected
      //age filter is derived from the request and then passed on to the underlying layer
      int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year;
      return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId)
      //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on.
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-14
      • 1970-01-01
      • 2012-11-12
      • 2015-06-25
      • 2011-05-18
      • 2017-03-29
      • 2014-08-29
      • 2014-01-02
      相关资源
      最近更新 更多