在有关TDD的文章中,我经常在重构步骤中读到“删除重复项”或“提高可读性”。但是,什么使我删除了未使用的功能?
例如,假设有一个C
带有方法a()
和的类b()
。现在我认为有一个f()
被驱动的方法会很不错C
。实际上,除定义/描述的单元测试外,f()
所有对的调用都将替换。不再需要-测试除外。b()
b()
删除b()
和使用它的所有测试是否保存?那是“提高可读性”的一部分吗?
在有关TDD的文章中,我经常在重构步骤中读到“删除重复项”或“提高可读性”。但是,什么使我删除了未使用的功能?
例如,假设有一个C
带有方法a()
和的类b()
。现在我认为有一个f()
被驱动的方法会很不错C
。实际上,除定义/描述的单元测试外,f()
所有对的调用都将替换。不再需要-测试除外。b()
b()
删除b()
和使用它的所有测试是否保存?那是“提高可读性”的一部分吗?
Answers:
当然是。最简单的代码是不存在的代码。
也就是说,重构通常意味着在不更改其行为的情况下改进代码。如果您认为有什么可以改善代码的,那就去做。在您被允许做之前,无需将其安装在某些鸽子洞中。
删除公共方法不是在“重构”-重构是在继续通过现有测试的同时更改实现。
但是,删除不需要的方法是完全合理的设计更改。
TDD在某种程度上将其引出了原因,因为在检查测试时,您可能会发现它在测试一种不需要的方法。测试正在驱动您的设计,因为您可以进行“看,该测试与我的目标无关”。
结合代码覆盖工具,它可能会在更高级别的测试中展现更多。如果您在覆盖代码的情况下运行集成测试,并且发现未调用方法,则可能是未使用方法的线索。静态代码分析还可以指示未使用方法。
有两种删除方法的方法;两者都在不同的情况下工作:
删除方法。遵循编译错误,删除所有相关代码和测试。如果您对受影响的测试是一次性的感到满意,请提交更改。如果不是,请回滚。
删除您认为过时的测试。使用代码覆盖范围运行整个测试套件。删除测试套件未执行的方法。
(这首先假设您的测试套件具有良好的覆盖范围)。
希望删除b()
不再使用的功能,出于同样的原因,也希望不要在一开始就添加未使用的功能。无论您称它为“可读性”还是其他,在其他所有条件相同的情况下,它都对代码进行了改进,因为它不包含任何无用的内容。为了至少有一种特定的措施(最好不要采用这种措施),将其删除可以保证更改后的未来维护成本为零!
我还没有发现需要通过测试来真正删除它的任何特殊技术,因为任何替换b()
新内容的想法当然都必须考虑当前调用的所有代码。b()
,并且测试是“所有代码”的子集”。
通常对我有用的推理方法是,在我注意到f()
已经b()
过时的那一点上,因此b()
至少应该弃用该术语,并且我希望查找所有的调用,b()
目的是将它们替换为f()
,我还考虑测试代码。具体来说,如果b()
不再需要,则可以并且应该删除其单元测试。
您说得很对,没有什么可以迫使我注意b()
不再需要的。这是一个技巧问题(而且,如苗条地说,高级测试的代码覆盖率报告)。如果仅进行单元测试,而没有功能测试,请参阅b()
,我可以谨慎地认为它不是任何已发布接口的一部分,因此对于不受我直接控制的任何代码,删除它都不是重大更改。
红色/绿色/重构周期未明确提及删除测试。此外,删除b()
显然违反了打开/关闭的原则,因为很明显,您的组件是可以修改的。因此,如果您想将此步骤视为简单TDD之外的东西,请继续。例如,您可能有一些声明测试“坏”的过程,在这种情况下,可以以删除测试为理由,因为它测试了不应该存在的内容(不必要的功能b()
)。
我认为在实践中,大多数人可能允许进行一定数量的重新设计以及红色/绿色/重构周期,或者即使严格来讲,他们也认为删除多余的单元测试是“重构”的有效部分。它不是重构。您的团队可以决定需要多少戏剧和文书工作来证明这一决定的合理性。
无论如何,如果b()
很重要,那么就可以对其进行功能测试,并且不能轻易删除它们,但是您已经说过,只有单元测试。如果您不能正确地区分单元测试(写到代码的当前内部设计,您已经更改了)和功能测试(写到已发布的接口,也许您不想更改),那么您需要更加谨慎关于删除单元测试。
要始终记住的一件事是,我们现在使用的是带有版本控制的代码存储库。被删除的代码实际上并没有消失……在上一个迭代中它仍然存在。所以把它吹走!使用Delete键要放宽手脚,因为您总是可以回去找回您认为某天可能有用的宝贵的优雅方法...如果有一天来过。在那。
当然,这伴随着不向后兼容的发行版的弊病和危险的警告……依赖于接口实现的外部应用程序,现在(突然)被弃用的代码所孤立。