我必须在单元测试中伪造一个值对象吗?
本文关键字:一个 对象 单元测试 伪造 | 更新日期: 2023-09-27 18:11:53
我使用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关于单元测试的书(《单元测试的艺术》)。在这里他说
"外部依赖(文件系统,时间,内存等)应该是替换为存根"
他所说的外部依赖是什么意思?我的值对象(范围)也是一个外部依赖,应该伪造?我应该伪造一个类拥有的所有依赖关系吗?
谁能给我一个建议
TL;DR
用最简单的方法解决你的问题。
我使用TDD的时间越长,我就越体会到实用主义的价值。编写超级独立的单元测试本身并没有什么价值。测试是为了帮助你编写高质量的代码,这些代码易于理解,并解决正确的问题。
如果您需要能够切换到另一个具体的范围实现而无需修改依赖于它的代码,则为range类添加接口是一个好主意。
但是,如果您不需要添加接口,则没有任何实际目的,但它确实增加了一些复杂性,实际上使您远离编写易于理解的代码来解决问题的目标。
注意不要过多考虑未来可能发生的变化。YAGNI是一个值得遵循的好原则。如果您一直在使用TDD,那么在将来出现实际需要时重构代码就不会有任何问题,因为您有可靠的测试可以依赖。
一般来说,我不认为一个合适的值对象是一个依赖项。如果它足够复杂,在测试时让其他代码使用它会让你感到不舒服,那么听起来它实际上更像是一个服务。单元测试应该独立运行(完全在内存中),而不必接触任何外部系统,如文件系统、数据库、web服务、邮件服务、系统时钟,或任何缓慢的、难以设置的或不确定的(如不断变化的系统时间)。
为了能够做到这一点,您应该抽象掉那些允许您在测试中模拟它们的外部依赖。
单元测试则更进一步。在单元测试中,您通常只想测试单个方法或单个类的逻辑。您对验证多个组件如何集成不感兴趣,而只想验证单个类的逻辑是否正确,以及它与其他组件是否正确通信。
为了能够做到这一点,您需要伪造那些其他组件(类的依赖项)。所以一般来说,你确实应该把类拥有的所有依赖项(包含行为)都去掉。