Moq,严格vs宽松使用

时间:2011-02-14 19:22:10

标签: c# .net unit-testing mocking moq

在过去,我只使用了Rhino Mocks,典型的严格模拟。我现在正在与Moq合作开展一个项目,我想知道正确的用法。

假设我有一个带有方法Bar的对象Foo,它在对象Buzz上调用Bizz方法。

在我的测试中,我想验证Bizz是否被调用,因此我觉得有两种可能的选择:

严格模拟

var mockBuzz= new Mock<IBuzz>(MockBehavior.Strict);
mockBuzz.Setup(x => x.Bizz()); //test will fail if Bizz method not called
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.VerifyAll();

松散的模拟

var mockBuzz= new Mock<IBuzz>();    
foo.Buzz = mockBuzz
foo.Bar();
mockBuzz.Verify(x => x.Bizz()) //test will fail if Bizz method not called

是否有标准或正常的方法?

4 个答案:

答案 0 :(得分:76)

当我第一次在单元测试中使用模拟时,我曾经使用过严格的模拟。这不会持续很长时间。我停止这样做有两个原因:

  1. 测试变得脆弱 - 使用严格的模拟,你会断言不止一件事,调用设置方法,并且不调用其他方法。当您重构代码时,测试通常会失败,即使您尝试测试的内容仍然是真的。
  2. 测试更难阅读 - 您需要为模拟调用的每个方法设置一个设置,即使它与您要测试的内容没有关系。当有人阅读此测试时,他们很难说出对测试重要的是什么,以及实施的副作用是什么。
  3. 由于这些原因,我强烈建议在单元测试中使用松散的模拟。

答案 1 :(得分:13)

我有C ++ /非.NET开发的背景,最近我更多地使用.NET,所以当我第一次使用Moq时,我有一定的期望。我试图理解WTF继续我的测试以及为什么我测试的代码抛出一个随机异常而不是Mock库告诉我代码试图调用哪个函数。所以我发现我需要打开严格的行为,这是令人困惑的 - 然后我遇到了这个问题,我看到的还没有勾选答案。

Loose 模式,以及它是默认 的事实是疯狂的 。 Mock库的重点是什么,它做了一些完全不可预测的事情,你还没有明确列出它应该做什么?

我完全不同意支持松散模式的其他答案中列出的要点。没有充分的理由使用它,我永远不会想要。在编写单元测试时,我想确定发生了什么 - 如果我知道函数需要返回null,我会让它返回。我希望我的测试变得脆弱(以重要的方式),以便我可以修复它们并添加到测试代码套件中的设置行,这些是明确的信息,它正在向我描述我的软件将会做什么。

问题是 - 是否有标准和正常的方式来做到这一点?

是的 - 从一般编程的角度来看,即其他语言和.NET世界之外,你应该始终使用Strict。善良知道为什么它不是Moq的默认值。

答案 2 :(得分:9)

我有一个简单的约定:

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

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

  3. 例如: 假设我们有数据库提供程序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.
    
    }
    

答案 3 :(得分:4)

我个人而言,不熟悉嘲笑和Moq认为从严格模式开始有助于更好地理解内部和正在发生的事情。 “松散”有时会隐藏细节并通过测试,而moq初学者可能无法看到。一旦你的模拟技能下降 - 松散可能会更有效率 - 就像在这种情况下使用“设置”保存一行,而只是使用“验证”。