【问题标题】:TDD - How to test .Read() without .Write()?TDD - 如何在没有 .Write() 的情况下测试 .Read()?
【发布时间】:2015-07-11 00:16:48
【问题描述】:

我们正在尝试使用 TDD 来创建我们的系统,但我们遇到了无法确定正确的 TDD 行动方案的情况。

我们已经将文件 IO 隐藏在一个接口后面,如下所示:

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

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

我们要确保这个InMemoryFileIo 正常工作,并且在某些情况下我们想用它来代替实际的文件系统,所以它应该是“生产质量”。

问题是,以“正确的 TDD 方式”做所有事情,我们如何为 .Read().Write() 创建不相互依赖的测试?

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

假设我们有两个测试,一个测试.Read(),另一个测试.Write()。如果其中任何一个功能不起作用,那么两个测试都会失败。这违反了“一个测试应该只有一个失败的原因”的原则。

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

【问题讨论】:

  • 在这种情况下很好,只需将它们一起测试 - 一个失败将意味着另一个失败,并且测试中的错误消息会告诉你在你可以的情况下哪个失败了查出。不要想太多,也不要试图使用任何虚假的模拟反射东西——它在这里是同一个单元内的,这是有道理的。
  • InMemoryFileIo 在其构造函数中采用 byte[] 而不是创建它(依赖注入)的“正确”方式(同样,不用担心)预先设置要读取的数据或对彼此外部写入的数据运行检查 - 真的,不要想太多 - 只需编写对您有意义的测试并帮助清楚地识别系统中的缺陷。
  • 我认为值得指出的是,如果您的内存 IO 类仅用作测试替身,那么它应该足够简单,无需进行大量测试。关键是对依赖于接口的其他类进行 TDD 处理是假的,对吗?将他们的通过或不通过测试与您的内存文件 IO fake 是否无错误相结合对我来说似乎是一个潜在的坏主意。我建议保持你的存根简单。

标签: unit-testing testing tdd


【解决方案1】:

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

Roy Osherove 定义单位为:

单元测试是一段自动化的代码,它调用系统中的一个工作单元,然后检查关于该工作单元行为的单个假设。工作单元是系统中的单个逻辑功能用例,可由某些公共接口(在大多数情况下)调用。一个工作单元可以跨越一个方法、一个整个类或多个协同工作的类,以实现一个可以验证的逻辑目的。

所以最后,抛开所有指导方针,如果为您划分阅读/写作职责没有意义,只需组合测试它们即可。总的来说,当我觉得没有干净/简单的方法可以通过后门验证测试结果时(无论是可以模拟的合作者,一些状态验证,......),我都可以使用一些公共 API 的其他功能。这些通过公共 API 运行您的组件的测试往往不如使用后门进行验证的测试那么脆弱。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-17
    • 2019-12-17
    • 1970-01-01
    • 2022-11-17
    相关资源
    最近更新 更多