2

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

我的对象的 API 如下:

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

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

  1. Sample为( 取决于Order和)初始化一些简单的属性SampleService

  2. Sample为(取决于Order.Samples和)设置一个新的唯一名称SampleService

  3. Sample为(取决于)设置一些附加字段SampleService

我的问题是,如果我想彻底测试该方法,我有很多不同的情况需要考虑一些复杂的设置。

我的第一个想法是提取一个类以生成点 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);
    }

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

您如何看待这种方法?

您能否提出其他替代方案以及您为什么要这样做?

4

2 回答 2

2

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

您应该只模拟访问外部资源的东西的假设是错误的。你模拟依赖关系,不管它们是什么。模拟的重点是将实际的类/方法逻辑与其他组件(依赖项)的操作细节隔离开来。

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

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

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

于 2013-10-18T09:15:32.937 回答
1

Unit testing is always a balancing act. Obviously, you want to test CreateSample() works as intended. As it interacts with Order and SampleService, then ideally you're tests would use instances of those objects in the tests. However, as you point out, that complicates the setup of the tests, which both creates extra work and discourages thorough testing.

One point to note about how you have structured you code is that Order and SampleService are concrete classes; you have coupled CreateSample() to those classes. If you have CreateSample() work with IOrder and ISampleService, you'll decouple the method from those other classes and thus the setup of tests will likely be easier.

Mocking your objects is a compromise. It can simplify testing, however, you have to be aware that your tests are not then testing your system, but a partial mock of your system. At times, this can help. It can also lead to a false sense of security though. You can end up testing all the parts, only to have the whole thing fall apart when you join those parts together.

于 2013-10-18T09:39:57.927 回答