对于Mock框架(例如NMock)与VS 2011 Fakes框架的区别有些困惑。
通过MSDN,我了解到Fakes允许您像RhinoMock或NMock一样模拟依赖项,但是方法不同,Fakes生成代码以实现此功能,而Mocks框架则没有。我的理解正确吗?是假货只是另一个Mock框架

最佳答案

您的问题是关于MS Fakes框架与NMock有何不同,看来其他答案已经解决了其中的一些问题,但是这里有一些有关它们如何相同和如何不同的更多信息。 NMock也类似于RhinoMocks和Moq,因此我将它们与NMock分组在一起。

我立即看到NMock / RhinoMocks / Moq与MS Fakes框架之间存在3个主要差异:

  • MS伪造框架使用生成的代码,就像在Visual Studio早期版本中的Accessors一样,而不是通用类型。当您要使用伪造的框架作为依赖项时,可以将包含依赖项的程序集添加到测试项目的引用中,然后右键单击它以生成测试 double (存根或填充)。然后,当您进行测试时,实际上是在使用这些生成的类。 NMock使用泛型来完成同一件事(即IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>())。我认为,MS Fakes框架方法禁止在测试中进行代码导航和重构,因为您实际上是在针对生成的类而不是实际界面进行操作。
  • MS造假框架提供存根和痣(垫片),而NMock,RhinoMocks和Moq都提供存根和模拟。我真的不理解MS决定不包含模拟游戏的决定,并且出于以下原因,我个人并不喜欢黑痣。
  • 使用MS Fakes框架,您可以提供要存根的方法的替代实现。在这些替代实现中,您可以指定返回值并跟踪有关如何或是否调用该方法的信息。使用NMock,RhinoMocks和Moq,您可以生成一个模拟对象,然后使用该对象指定存根返回值或跟踪交互(是否以及如何调用方法)。我发现MS伪造方法更加复杂且表达能力较差。

  • 为了澄清框架提供的差异:NMock,RhinoMocks和Moq都提供两种类型的测试双打(存根和模拟)。伪造品框架提供存根和痣(它们称它们为垫片),但不幸的是不包含模拟。为了了解NMock和MS Fakes之间的区别和相似之处,有助于理解这些不同类型的测试重复是什么:

    存根:存根在需要提供方法或属性的值时使用,该值将由被测方法要求您的测试加倍。例如,当我的被测试方法调用IStudentRepository测试double的DidSStudentExist()方法时,我希望它返回true。

    NMock和MS伪造品中的存根的想法是相同的,但是使用NMock,您将执行以下操作:
    Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));
    

    借助MSFakes,您可以像这样进行操作:
    IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
    {
        DoesStudentExistInt32 = (studentId) => { return new Student(); }
    };
    

    注意,在“MS Fakes”示例中,您为DosStudentExist方法创建了一个全新的实现(请注意,它被称为“DoesStudentExistInt32”,因为在其生成存根对象时,fakes框架会将参数数据类型附加到方法名称中,我认为这模糊了测试)。老实说,NMock实现也使我感到烦恼,因为它使用字符串来标识方法名称。 (如果我误解了NMock的预期用途,请原谅我。)这种方法确实抑制了重构,出于这个原因,我强烈推荐RhinoMocks或Moq优于NMock。

    Mocks: Mocks用于验证被测方法及其依赖项之间的交互。使用NMock,您可以通过设置类似于以下内容的期望值来实现:
    Expect.Once.On(mockStudentRepository).Method("Find").With(123);
    

    这是为什么我更喜欢RhinoMocks和Moq而不是NMock的另一个原因,NMock使用了较早的期望样式,而RhinoMocks和Moq都支持Arrange / Act / Assert方法,在这种方法中,您可以像这样在测试结束时将期望的交互指定为断言:
    stubStudentRepository.AssertWasCalled( x => x.Find(123));
    

    再次注意,RhinoMocks使用lambda而不是字符串来标识该方法。 ms fakes框架根本不提供模拟。这意味着在存根实现中(请参阅上面的存根描述),您必须设置变量,稍后验证它们是否正确设置。看起来像这样:
    bool wasFindCalled = false;
    
    IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository()
    {
        DoesStudentExistInt32 = (studentId) =>
            {
                wasFindCalled = true;
                return new Student();
            }
    };
    
    classUnderTest.MethodUnderTest();
    
    Assert.IsTrue(wasFindCalled);
    

    我发现这种方法有些复杂,因为您必须在存根中跟踪调用,然后在测试中稍后进行断言。我发现NMock(尤其是RhinoMocks)的示例更具表现力。

    痣(Shims):坦率地说,我不喜欢痣,因为它们可能被滥用。我非常喜欢单元测试(尤其是TDD)的一件事是,测试代码可以帮助您了解编写不良代码的位置。这是因为测试写得不好的代码很困难。使用摩尔时,情况并非如此,因为摩尔实际上是设计用来允许您针对未注入(inject)的依赖项进行测试或测试私有(private)方法。它们的工作方式与存根类似,不同的是您使用ShimsContext这样:
    using (ShimsContext.Create())
    {
        System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
    }
    

    我对垫片的担心是,人们会开始将它们视为“一种更简单的单元测试方法”,因为它不会强制您按照应有的方式编写代码。有关此概念的更完整文章,请参阅我的这篇文章:
  • https://jcoop.io/2012/05/21/solid-code-for-solid-reasons/

  • 有关与伪造框架有关的某些问题的更多信息,请查看以下文章:
  • https://jcoop.io/2012/03/16/38/

  • 如果您有兴趣学习RhinoMocks,请观看以下Pluralsight培训视频(完整披露-我编写了本类(class),并获得了稿酬,以获取观点,但我认为它适用于本次讨论,因此在此进行了介绍):
  • http://www.pluralsight.com/training/Courses/TableOfContents/rhinomock-fundamentals
  • 关于unit-testing - 模拟框架与MS Fakes框架,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9677445/

    10-12 17:29