TDD - 如何在没有 .Write() 的情况下测试 .Read()?

TDD - How to test .Read() without .Write()?

我们正在尝试使用 TDD 来创建我们的系统,但我们遇到了一种情况,我们无法弄清楚正确的 TDD 行动方案是什么。

我们已将文件 IO 隐藏在如下接口后面:

public interface IFileIo
{
    byte[] Read(string fileName);
    void Write(string filename, byte[] data);
}

现在我们正在创建一个 InMemoryFileIo,我们可以用它来代替我们将用于生产的真实 SystemFileIo class。

我们想确保这个 InMemoryFileIo 正常工作,并且在某些情况下我们想用它来代替实际的文件系统,所以它应该是 "production quality".

问题是,在完成 "right TDD way" 的所有操作后,我们如何为 .Read().Write() 创建一个彼此不依赖的测试?

为了测试 .Read() 是否正常工作,我们需要先成功调用 .Write(),同样,为了测试 .Write() 是否正常工作,我们会之后需要调用 .Read() 。通过这样做,我们实际上已经创建了两次相同的测试(安排,然后是写入,然后是读取,然后是断言)。

假设我们有两个测试,一个测试 .Read(),一个测试 .Write()。如果这些功能中的任何一个不起作用,那么两个测试都会失败。这违反了"A test should only have one reason to fail".

的原则

这里的示例是针对文件 IO 的,但是在使用数据库时我们也遇到了同样的问题(测试 put 没有 get)。

整个问题归结为您如何准确定义 "units"。就我个人而言,将 "unit" 视为一组连贯的行为而不是单个方法调用会有所帮助。

Roy Osherove 定义单位为:

A unit test is an automated piece of code that invokes a unit of work in the system and then checks a single assumption about the behavior of that unit of work. A unit of work is a single logical functional use case in the system that can be invoked by some public interface (in most cases). A unit of work can span a single method, a whole class or multiple classes working together to achieve one single logical purpose that can be verified.

所以最后,抛开所有准则,如果为您拆分 reading/writing 职责没有意义,只需将它们结合起来进行测试。一般来说,当我觉得没有 clean/easy 方法可以通过后门验证测试结果时(无论是可以模拟的合作者,还是一些状态验证,......),我都没有问题行使 public API 的一些其他功能。这些通过 public API 测试您的组件的测试往往比那些使用后门进行验证的测试更不脆弱。