使用模拟来减少方法周围的测试次数

时间:2013-10-18 09:01:21

标签: c# unit-testing mocking

我希望对测试一个类的不同选项有一些见解,该类有责任在给定两个其他对象的情况下创建一个Sample对象。

我的对象的API如下:

public interface ISampleCreator
{
    Sample CreateSample(Order order, SampleService sampleService);
}

此方法中的逻辑可分为三个部分:

  1. 初始化Sample的一些简单属性(取决于OrderSampleService)。

  2. Sample设置一个新的唯一名称(取决于Order.SamplesSampleService)。

  3. Sample设置一些其他字段(取决于SampleService)。

  4. 我的问题是,如果我想彻底测试这个方法,我会在很多不同的情况下考虑一些复杂的设置。

    我的第一个想法是提取一个类来为第2点生成唯一的名称,并提取一个类来为该样本生成其他字段。

    然后我可以独立测试这两个类,我可以模拟它们以减少对CreateSample方法的测试。例如:

        [TestMethod]
        public void CreateSampleTest()
        {
            Order order = new Order();
            SampleService sampleService = new SampleService();
            Mock<ISampleNameGenerator> mockNameGenerator = new Mock<ISampleNameGenerator>();
            mockNameGenerator.Setup(x => x.GenerateSampleName(order.Samples, sampleService))
                             .Returns("Generated name");
            Mock<ISampleFieldsCreator> mockFieldsCreator = new Mock<ISampleFieldsCreator>();
            List<SampleField> sampleFields = new List<SampleField>();
            mockFieldsCreator.Setup(x => x.CreateFieldsForNewSample(sampleService))
                             .Returns(sampleFields);
    
            SampleCreator sampleCreator = new SampleCreator(mockNameGenerator.Object, mockFieldsCreator.Object);
    
            Sample sample = sampleCreator.CreateSample(order, sampleService);
    
            Assert.AreEqual("Generated name", sample.Name);
            Assert.AreEqual(sampleFields, sample.Fields);
            Assert.AreEqual(order, sample.Order);
            Assert.AreEqual(sampleService, sample.SampleService);
        }
    

    我对这种方法的问题是,我正在模拟我拥有的类,并且不访问任何外部资源:基本上,我正在进行基于模拟的测试,以简化测试的设置。

    您如何看待这种方法?

    您能否提出其他替代方案以及为何会这样做?

2 个答案:

答案 0 :(得分:2)

  

我使用这种方法的问题是,我正在模拟我拥有的类,并且不能访问任何外部资源:基本上,我正在进行基于模拟的测试,作为简化测试设置的一种方法。

假设您应该只模拟加入外部资源的东西是错误的。你模拟依赖,无论它们是什么。模拟的目的是将实际的类/方法逻辑与其他组件的操作细节隔离开来(依赖)。

看看你介绍的重构 - 提取唯一名称生成到单独的类。这听起来很合理,因为该过程可能涉及全新的限制和内部知识,而您的原始对象构建方法并不需要拥有。

解耦代码通常是个好主意,因为它会导致更多单任务导向的类,更容易的测试,更容易的对象组合,更多的代码重用。单元测试通常有助于以您发现的方式完全发现这些改进区域 - 使测试变得困难/过于复杂而无法首先编写。

长话短说 - 你的方法是正确的。

答案 1 :(得分:1)

单元测试始终是一种平衡行为。显然,您希望测试CreateSample()按预期工作。当它与Order和SampleService交互时,理想情况下,您的测试将在测试中使用这些对象的实例。但是,正如您所指出的那样,这会使测试的设置变得复杂,这会产生额外的工作并阻碍彻底的测试。

关于如何构建代码的一点要注意的是Order和SampleService是具体的类;您已将CreateSample()与这些类相结合。如果你有CreateSample()使用IOrder和ISampleService,你将把方法与其他类分离,因此测试的设置可能会更容易。

嘲弄你的物品是妥协。它可以简化测试,但是,您必须意识到您的测试不是测试您的系统,而是部分模拟您的系统。有时,这可以帮助。它也可能导致虚假的安全感。您可以最终测试所有部件,但只有当您将这些部件连接在一起时,整个部件才会崩溃。