36

最近发现了这种开发方法,我发现它是一种相当不错的方法。所以,对于我的第一个项目,我有一个小的 DLL 代码(在 C#.NET 中,因为它的价值),我想为这个代码做一组测试,但我有点迷茫如何和从哪儿开始。

我正在使用 NUnit 和 VS 2008,任何关于从何种类开始、为什么编写测试以及一般如何将代码移动到基于测试的开发的任何提示都将不胜感激。

4

5 回答 5

56

请参阅Michael Feathers所著的《有效处理遗留代码》一书。

总之,将现有代码重构为可测试和经过测试的代码需要做很多工作;有时工作量太大而不能实用。这取决于代码库有多大,以及各种类和函数相互依赖的程度。

没有测试的重构将引入行为的变化(即错误)。纯粹主义者会说这并不是真正的重构,因为缺乏测试来检查行为是否没有改变。

与其一次性向整个应用程序全面添加测试,不如在代码区域工作时添加测试。很可能您将不得不再次返回这些“热点”。

自下而上添加测试:测试小的、独立的类和函数的正确性。

自上而下添加测试:将整个子系统作为黑盒进行测试,看看它们的行为是否会随着代码的变化而变化。因此,您可以逐步查看它们以了解发生了什么。这种方法可能会给你带来最大的好处。

在添加测试时不要太在意“正确”行为是什么,寻找并避免行为变化。未经测试的大型系统通常具有看似不正确但系统的其他部分依赖的内部行为。

考虑隔离数据库、文件系统、网络等依赖项,以便在测试期间将它们替换为模拟数据提供者。

如果程序没有内部接口,即定义一个子系统/层与另一个子系统/层之间边界的线,那么您可能必须尝试引入这些,并对其进行测试。

此外,像RhinomocksMoq这样的自动模拟框架可能有助于在这里模拟现有的类。在为可测试性设计的代码中,我还没有真正发现对它们的需求。

于 2008-10-03T14:20:45.157 回答
11

我称之为“测试驱动的逆向工程”。

从“底部”开始——每个类都可以单独检查并为其编写测试。如有疑问,请猜测。

当您正向执行普通的 TDD 时,您将测试视为神圣的,并假设代码可能已损坏。有时测试是错误的,但你的出发点是它是代码。

当你在做 TDRE 时,代码是神圣的——直到你能证明代码有一个长期存在的错误。在相反的情况下,您围绕代码编写测试,调整测试直到它们工作并声称代码工作。

然后,您可以深入研究错误的代码。一些糟糕的 cade 会有合理的测试用例——这只是需要清理。然而,一些糟糕的代码也会有一个毫无意义的测试用例。这可能是您可以纠正的错误或笨拙的设计。

要判断代码是否真的错误,您还需要从整体测试用例开始。真正有效的实时数据是一个开始。此外,产生任何已知错误的实时数据也是一个很好的起点。

我编写了一些代码生成器来将实时数据转换为单元测试用例。这样,我就有了一致的测试和重构基础。

于 2008-10-03T17:10:40.837 回答
10

在将未经测试的代码迁移到单元测试环境中时,有效地使用遗留代码是我的圣经,它还提供了很多关于什么使代码易于测试以及如何测试它的见解。

我还发现Test Driven Development by ExamplePragmatic Unit Testing: in C# with NUnit是对该环境中单元测试的一个不错的介绍。

启动 TDD 的一种简单方法是从今天开始首先编写测试,并确保无论何时需要接触现有(未经单元测试)的代码,在更改之前编写通过测试来验证系统的现有行为这样您就可以在之后重新运行这些测试,以增加您没有破坏任何东西的信心。

于 2008-10-03T14:24:26.600 回答
5

可测试的代码很容易被发现 - 通过附带的测试。如果有的话,它必须是可测试的。如果没有 - 假设相反。;)

也就是说:测试驱动开发(TDD)与其说是一种测试策略,不如说是一种设计策略。您首先编写的测试有助于设计类的接口,以及正确确定类(或子系统)的范围。

拥有您在 TDD 期间创建的测试并在以后执行它们是很好的测试,但这只是该设计理念的一个(非常受欢迎的)副作用。

这就是说,预计您的代码会对测试产生一些阻力。聆听您的代码并更改界面以便于测试。当您开始编写测试时,您很可能会重新设计它。

于 2008-10-03T14:24:24.073 回答
2

您的 DLL 提供某种服务。对于每一个服务,在获取这个服务之前你要做什么,你应该传递什么参数来获取这个服务,你怎么知道请求的服务已经正确执行了?

一旦你有了这些问题的答案,你就可以编写第一个测试。这样的测试宁愿被称为特性测试而不是单元测试,但如果 DLL 不是使用 TDD 开发的,它可能比单元测试更容易编写。

M. Feathers 的“有效地使用遗留代码”中也讨论了表征测试,在其他回复中也推荐了这一点。

此外,请务必在添加任何新代码行之前编写失败的测试。

于 2008-10-03T17:02:10.730 回答