我是否必须在单元测试中伪造一个值对象

时间:2013-05-02 11:48:47

标签: c# unit-testing dependency-injection tdd

我使用包含方法(测试中的方法)的TDD编写了一个类,该方法将一个简单的值对象作为参数(range)。

代码:

测试中的方法如下所示:

public List<string> In(IRange range)
{
     var result = new List<string>();
     for (int i = range.From; i <= range.To; i++)
     {
          //...
     }
     return result;
}

此外,我还有一个单元测试来验证我的测试方法:

[TestMethod]
public void In_SimpleNumbers_ReturnsNumbersAsList()
{
    var range = CreateRange(1, 2);
    var expected = new List<string>() { "1", "2" };
    var result = fizzbuzz.In(range);
    CollectionAssert.AreEqual(expected, result);
}

private IRange CreateRange(int from, int to)
{
    return new Fakes.StubIRange() 
    { 
        FromGet = () => { return from; }, 
        ToGet = () => { return to; } 
    };
}

问题:

我读过Roy Osherove关于单元测试的书(“单元测试的艺术”)。在那里他说

  

“外部依赖(文件系统,时间,内存等)应该是   替换为存根“

外部依赖关系是什么意思?我的值对象(范围)也是一个应该伪造的外部依赖吗?我应该伪造一个类的所有依赖项吗?

有人可以给我一个建议

2 个答案:

答案 0 :(得分:3)

单元测试应该独立运行(完全在内存中)而不必触及任何外部系统,例如文件系统,数据库,Web服务,邮件服务,系统时钟或任何缓慢,难以设置的任何外部系统,或不确定性(例如不断变化的系统时间)。

为了能够做到这一点,你应该抽象出那些允许你在测试中模拟它们的外部依赖项。

然而,单元测试又向前迈进了一步。在单元测试中,您通常只想测试单个方法或单个类的逻辑。您不想验证多个组件如何集成,但您只想验证该单个类的逻辑是否正确,以及它是否与其他组件正确通信。

为了能够做到这一点,你需要伪造那些其他组件(类的依赖项)。所以一般来说,你应该伪造一个类所有的依赖关系(包含行为)。

答案 1 :(得分:3)

TL; DR

做最简单的事情可以解决你的问题。


我使用TDD的时间越长,我越了解实用的价值。编写超级孤立的单元测试本身并不具有价值。这些测试可以帮助您编写易于理解的高质量代码并解决正确的问题。

如果您需要能够切换到另一个具体范围实现而不必修改依赖于它的代码,那么为范围类添加接口是个好主意。

但是,如果你没有那个需要添加一个接口没有任何实际意义,但它确实增加了一些复杂性,这实际上使你从编写易于理解的代码的目标进一步解决问题。

小心不要过多考虑将来会发生什么变化。 YAGNI是一个很好的原则。如果您一直在进行TDD,那么如果将来出现实际需求,您将无法重构代码,因为您有可靠的测试依赖。

一般而言,我不认为正确的值对象是依赖关系。如果它足够复杂,让你感到不舒服,让其他代码在测试时使用它,听起来它实际上更像是一种服务。