何时使用模拟框架?

所以我正在玩我的unit testing的模拟框架(Moq),并且想知道何时应该使用模拟框架?

以下两个测试之间的好处/缺点是什么:

public class Tests { [Fact] public void TestWithMock() { // Arrange var repo = new Mock(); var p = new Mock(); p.Setup(x => x.Id).Returns(1); p.Setup(x => x.Name).Returns("Joe Blow"); p.Setup(x => x.AkaNames).Returns(new List { "Joey", "Mugs" }); p.Setup(x => x.AkaNames.Remove(It.IsAny())); // 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(); var p = new Person { Id = 1, Name = "Joe Blow", AkaNames = new List { "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"); } } 

使用模拟对象来真正创建一个unit testing – 一个假设所有依赖项都能正常运行的测试,所有你想知道的是SUT(被测系统 – 一种说你正在测试的类的奇特方式) 。

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

当您测试具有慢依赖性的对象(如数据库或Web服务)时,模拟对象尤其重要。 如果您真的打了数据库或进行真正的Web服务调用,那么您的测试将花费更多时间来运行。 只需几秒钟就可以忍受,但是当您在持续集成服务器中运行数百次测试时,这种情况会非常快,并且会降低您的自动化程度。

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

我使用一些规则来编写unit testing。

  1. 如果我的被测系统(SUT)或测试对象有依赖关系,那么我将它们全部模仿。
  2. 如果我测试一个返回结果的方法,那么我只检查结果。 如果依赖关系作为方法的参数传递,则应该模拟它们。 (见1)
  3. 如果我测试’void’方法,那么validation模拟是测试的最佳选择。

Martin Fowler的一篇旧文章Mocks Are Not Stubs 。

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

此外,我看到一些导致您问题的设计问题。

如果允许从AkaNames集合中删除AkaName ,则可以使用存根并检查该人的状态。 如果将特定方法void RemoveAkaName(string name)Person类中,则应使用mocks以validation其调用。 并且应该将RemoveAkaName逻辑作为Person类测试的一部分进行测试。

我会使用stub for product和mock for repository for code。

模拟框架用于删除依赖关系,因此unit testing将关注要测试的“单元”。 在你的情况下,这个人看起来像一个简单的实体类,没有必要使用Mocking。

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

模拟用于测试无法隔离运行的对象。 假设函数A依赖于函数B,对函数A执行unit testing,我们甚至最终测试函数B.通过使用mock,您可以模拟函数B的function,测试只能集中在函数A上。

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

一个更好的用例是,如果您有一个存储库调用数据库。 从unit testing的角度来看,希望模拟db调用并返回预定数据。 这样做的主要优点是消除了对退出数据的依赖性,但也降低了性能,因为db调用会降低测试速度。