Moq,严格vs宽松使用

Bry*_*owe 49 .net c# unit-testing moq mocking

在过去,我只使用了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();
Run Code Online (Sandbox Code Playgroud)

随着一个松散的模拟

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

有这样做的标准或正常方式吗?

And*_*wry 75

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

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

因此,我强烈建议在单元测试中使用松散的模拟.

  • -1这就是严格模拟更好的原因,因为严格模拟明确描述了依赖项会发生什么。如果同一依赖对象由许多其他意外调用触发,则验证一个特定的调用是否发生是没有意义的。如果测试变得过于复杂,那不是严格模拟的错,但这是必须重新设计代码或测试体系结构的一个好兆头。松散的模拟对于特定情况是一个很好的工具,但是这个答案表明的一般说法适得其反,并且违反了测试驱动的开发原则。 (3认同)
  • @TiborTakács Uhm 从我读到的经验来看,使用 Srict 实际上对于 TDD 来说会适得其反,根据我的经验也是如此。老实说,我不明白你如何从这里得到“这正是严格模拟更好的原因”...... (3认同)

Ben*_*ict 18

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

宽松的模式,而事实上,它是默认的是疯狂的.Mock库的重点是什么,它完全不可预测,你没有明确列出它应该做什么?

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

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

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

  • 实际上,这甚至都不是真的.严格成为问题的唯一方法是您的代码不遵循SOLID.否则,如果变化太大,您希望测试中断的任何更改. (3认同)
  • 我一直在试图弄清为什么人们最近也不喜欢Strict。我得出的结论是,想要使用Loose的人在同一个类中有许多公共方法调用其他公共方法。发生这种情况时,即使您只想专注于一种特定的方法,您最终还是会遇到破坏所有测试的类重构。就是说,对我来说,“宽松模式=懒惰”是因为您怎么能相信那些测试正在完成您打算做的事情?如果嘲笑的默认值将来会发生变化并且它们都被破坏了怎么办。如果严格,那是不可能的。 (2认同)

Amo*_*mol 9

我有一个简单的约定:

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

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

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

数据访问界面如下所示:

public Student GetStudentById(int id);
public IList<Student> GetStudents(int ageFilter, int classId);
Run Code Online (Sandbox Code Playgroud)

使用此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.

}
Run Code Online (Sandbox Code Playgroud)


mit*_*daa 5

就我个人而言,作为嘲笑和最小起订量的新手,我觉得从严格模式开始有助于更好地理解内部结构和正在发生的事情。“松散”有时会隐藏细节并通过最小起订量初学者可能看不到的测试。一旦你掌握了你的模拟技巧 - 松散可能会更有效率 - 就像在这种情况下用“设置”保存一行并只使用“验证”代替。