我有一个很大的代码库,我需要对其进行重构以使其更好地契合在一起(一个大问题是每个组件之间的边界不够清晰)。由于将需要进行一些回归测试来验证任何重构,因此有意义的是:


为特定代码段创建单元测试
运行单元测试并确保它通过
重构代码并检查单元测试是否仍然通过

开始使用TDD方法进行此重构过程似乎是一个好主意,尽管我最大的担心是我在现有代码之后编写单元测试(而不是在单元测试之后编写代码)。

因为我在编写代码后编写单元测试,所以我应该首先重构,做自己的回归测试,然后继续使用TDD步骤(编写失败的测试,编写通过的代码,重构,重复)吗?

#1 楼

我建议阅读两本书,因为它们将有助于将遗留代码转移到TDD:

.Net中有效地使用旧版代码和Brownfield应用程序开发(新书,但基于.NET)。


由于我在编写代码后编写单元测试,因此我应该首先重构,进行自己的回归测试,然后继续使用TDD步骤(编写失败的测试,编写通过的代码,重构) ,重复)?


重构之前,请编写测试。在开始对其进行修改之前,测试将更多地属于规范(代码执行X)。这样,当您重构代码时,测试将与现有代码匹配,因此,如果重构破坏了某些内容,则会使现有客户感到不满意。

以上两本书将为您提供更多有关如何决定将内容分解为更小(更易于管理)的内容以及何时不进行处理的想法。

评论


太好了,谢谢,我会考虑买那本书。

–惊喜
2011年6月7日在13:39

#2 楼

IMO,您应该在进行任何重构之前继续编写用于传递语句的单元测试。原因是因为您希望在进行更改之前处于已知状态。

评论


同意这是自动化测试的最佳选择:界面没有改变,实现没有改变,代码应该像以前一样工作。

–user246
2011年7月3日在22:24

#3 楼

我认为Stacy称其为“抽象分支”

对于遗留代码,我发现最好的方法是在代码中找到关键点,在这些地方可以轻松插入覆盖输入和输出的接口。

复制您的第一个实现,进行重构,然后提供一个回归实现,该实现可以运行旧方法和新方法,并在发现任何差异时立即停止运行。

我们设法无缝地替换了我的直觉告诉我的一些事情,无论我多么努力尝试都会遇到麻烦,但这都没问题。

(*注意非功能性像性能一样有差异!)

#4 楼

您需要测试以表明重构没有破坏任何东西。

我是@Squirrel描述的基于AB理论的测试的忠实粉丝。

我也是Michael Feathers的《有效地使用旧版代码》一书是特征测试的忠实拥护者。

就个人而言,这是我90%的遗留代码工作中使用的技术(这是特征测试的一种变体)。


减少功能,
运行,
通过批准测试(www.approvaltests.com)捕获结果。

步骤1:
想象您正在重构一堆功能代码。这表示;所有使用的参数都传入,没有副作用,始终是确定性的(纯净的);返回所有有用的结果。例如,您有一个string Foo(int a, boolean b)方法;
这很容易测试,只需输入一些数据,并按照步骤2和3进行操作,最后要进行漂亮的表征测试。

当然,这不是真实世界,在真实世界中,我会得到类似的东西:void Foo()

所以首先我要弄清楚它的副作用是什么。它们可能是以下各项的组合:
1.更改全局变量,
2.更改数据库,
3.更改文件,
4.进行Web API调用。

,那么我将所有这些更改记录下来:
>
然后运行它,看看会发生什么,它可能不是确定性的,因为您没有传入任何变量,因此当您开始查看正在发生的情况时,便可以开始查看需要传入的内容假设它是一个数据库。那么您需要有效地进行回滚。

Log(sql)

Statement.Execute(sql)