在unit testing中重复代码更容易?
前段时间,我经历了几次unit testing,并重构了它们,使它们变得更加干燥 – 每个testing的意图都不再清晰。 在testing的可读性和可维护性之间似乎有一个折衷。 如果我在unit testing中留下重复的代码,它们更具可读性,但是如果我更改了SUT ,我将不得不追踪并更改每个副本的重复代码。
你是否同意这种权衡存在? 如果是这样,你喜欢你的testing是可读或可维护的吗?
重复的代码在unit testing代码中就像在其他代码中一样。 如果在testing中有重复的代码,那么重构实现代码将变得更加困难,因为您有不成比例的更新testing。 testing应该帮助你自信地重构,而不是成为阻碍你在被testing代码上工作的一个大的负担。
如果重复在夹具中,请考虑更多地使用setUp
方法或提供更多(或更灵活)的创build方法 。
如果重复是在操纵SUT的代码中,那么问自己为什么多个所谓的“单元”testing正在行使完全相同的function。
如果重复是在断言,那么也许你需要一些自定义断言 。 例如,如果多个testing有一系列的断言,如:
assertEqual('Joe', person.getFirstName()) assertEqual('Bloggs', person.getLastName()) assertEqual(23, person.getAge())
那么也许你需要一个assertPersonEqual
方法,这样你就可以编写assertPersonEqual(Person('Joe', 'Bloggs', 23), person)
。 (或者你可能只需要在Person
上重载相等运算符。)
正如你所提到的,testing代码是可读的。 特别是testing的目的是很重要的。 我发现如果很多testing看起来大致相同(例如,四分之三的线条相同或基本相同),很难发现和认识到显着的差异,而不仔细阅读和比较它们。 所以我发现重构删除重复有助于可读性,因为每个testing方法的每一行都与testing目的直接相关。 这对读者来说比直接相关的线条的随机组合更有帮助,线条只是样板。
也就是说,有时候testing是在类似的复杂情况下进行,但仍然有很大的不同,很难find减less重复的好方法。 使用常识:如果您觉得testing是可读的,并且意图清晰,并且在重构testing所调用的代码时可能需要更新理论上最less的testing,那么接受这个不完善并移动更有生产力的东西。 以后,当灵感来袭时,你总是可以回来重构testing。
可读性对于testing更重要。 如果一个testing失败,你希望问题是显而易见的。 开发人员不应该通过大量的考虑严格的testing代码来确定失败。 你不希望你的testing代码变得如此复杂以至于你需要编写unit testingtesting。
但是,消除重复通常是件好事,只要不会掩盖任何东西,并且消除重复testing可能会导致更好的API。 只要确保你不会越过收益递减点。
实施代码和testing是不同的动物,保理规则适用于他们。
重复的代码或结构总是在实现代码中的气味。 当你开始实施样板时,你需要修改你的抽象。
另一方面,testing代码必须保持一定程度的重复。 testing代码中的重复实现了两个目标:
- 保持testing解耦。 过度的testing耦合可能会使得难以更改由于合同改变而需要更新的单个失败testing。
- 保持testing有意义的孤立。 当单个testing失败时,必须合理地直截了当地发现它正在testing的内容。
只要每种testing方法都比20行更短,我倾向于忽略testing代码中的微不足道的重复。 在testing方法中,我喜欢安装运行validation节奏。
当在testing的“validation”部分中重复爬行时,定义自定义断言方法通常是有益的。 当然,这些方法仍然必须testing一个明确的关系,这个关系可以在方法名中显而易见: assertPegFitsInHole
– > good, assertPegIsGood
– > bad。
当testing方法变长且重复时,我有时会发现定义填充空白testing模板需要一些参数是有用的。 然后将实际的testing方法简化为使用适当的参数调用模板方法。
对于编程和testing中的很多事情,没有一个明确的答案。 你需要培养一种品味,最好的方法就是犯错误。
我同意。 交易存在,但在不同的地方是不同的。
我更可能重构重复代码设置状态。 但不太可能重构实际运行代码的testing部分。 这就是说,如果行使代码总是需要几行代码,那么我可能认为这是一种气味,并重构实际的testing代码。 这将提高代码和testing的可读性和可维护性。
您可以使用几种不同风格的testing实用程序方法来减less重复。
我在testing代码中比在生产代码中更容忍重复,但是我有时候对它感到沮丧。 当你改变一个class级的devise,你必须回头调整10个不同的testing方法,所有的testing方法都做相同的设置步骤,这是令人沮丧的。
我喜欢rspec因为这个:
它有两件事可以帮助 –
-
共享示例组来testing常见行为。
您可以定义一组testing,然后在您的真实testing中设置“包含”。 -
嵌套上下文。
你可以基本上为你的testing的一个特定子集有一个“设置”和“拆卸”的方法,而不仅仅是这个类中的每一个。
.NET / Java /其他testing框架越早采用这些方法,越好(或者可以使用IronRuby或JRuby编写testing,我个人认为这是更好的select)
Jay Fields创造了“DSLs应该是DAMP,而不是DRY”这句话,其中DAMP意味着描述性和有意义的短语 。 我想这同样适用于testing。 显然,太多的重复是不好的。 但是不惜一切代价去除重复就更糟了。 testing应该充当意图揭示的规范。 例如,如果您从几个不同的angular度指定相同的function,则可能会出现一定程度的重复。
理想情况下,unit testing一旦写入就不应该改变,所以我会倾向于可读性。
使unit testing尽可能离散也有助于使testing集中在他们所针对的特定function上。
有了这个说法,我倾向于尝试重复使用一些反复使用的代码,例如在一组testing中完全相同的代码。
“重构它们使它们更加干燥 – 每个testing的意图都不再明确”
这听起来像你在重构过程中遇到了麻烦。 我只是猜测,但如果结果不太清楚,那是不是意味着你还有更多的工作要做,所以你有相当优雅的testing,这是完全清楚的?
这就是为什么testing是UnitTest的一个子类 – 所以你可以devise出正确,易于validation和清除的好的testing套件。
在过去,我们拥有使用不同编程语言的testing工具。 devise愉快,易于工作的testing很难(或不可能)。
无论您使用哪种语言,您都拥有完整的权力 – Python,Java,C# – 所以请使用该语言。 您可以获得清晰且不太冗余的好看的testing代码。 没有任何折衷。
我不认为有更多的重复和可读的代码之间的关系。 我认为你的testing代码应该和其他代码一样好。 非重复的代码更好的可读性,然后重复的代码。
我觉得testing代码需要类似的工程,通常会应用于生产代码。 当然可以有赞成可读性的论据,我同意这很重要。
但是,根据我的经验,我发现考虑周全的testing更容易阅读和理解。 如果有5个testing看起来都是相同的,除了一个变化的variables和最后的断言,可能很难find那个单个不同的项目。 同样,如果这个因素被考虑因此只有变化的variables是可见的和断言的,那么很容易找出testing正在做什么。
在testing时find合适的抽象级别可能很困难,我认为这是值得的。