我目前有10个测试,只要路径或墙上有一块,我的俄罗斯方块片就不会向左移动。现在,我将不得不为正确的运动测试相同的行为。
如果我只是复制我已经拥有的左侧移动的10个测试并且仅进行所需的更改并且对代码本身执行相同的操作,那是不是太糟糕了?或者,如果逻辑基本相同,我应该从头开始再进行每次测试吗?
答案 0 :(得分:30)
尝试采用您未提及的第3种方法,即重构代码,以便在所有10次测试之间共享测试的一个实现。
jist是,复制代码几乎总是错误的做法。在此示例中,您可以将检查代码重构为一个名为IsTetrisPieceUnableToMoveLeftBecauseOfAPieceOrAWall
的方法。在为单元测试编写一些“共享”功能时,我总是寻找非常具有描述性的方法名称,因为它使得它非常清楚正在进行/测试的内容。
答案 1 :(得分:25)
我对这个问题有一点争议。虽然在生产代码中必须尽可能避免代码重复,但这对于测试代码来说并不是那么糟糕。生产和测试代码性质和意图不同:
生产代码可以提供一些复杂性,以便易于理解/维护。您希望代码处于正确的抽象级别,并使设计保持一致。这是好的,因为你有测试,你可以确保它的工作。如果您在逻辑级别实际拥有100%的代码覆盖率,则生产代码中的代码重复不会成为问题。这很难实现,因此规则是:避免重复并最大化代码覆盖率。
测试代码必须尽可能简单。您必须确保测试代码实际测试它应该是什么。如果测试很复杂,你可能会在测试或错误测试中遇到错误 - 而且你没有测试测试,所以规则是:保持简单。如果测试代码是重复的,那么当它发生变化时,这不是一个大问题。如果更改仅在一个测试中应用,则另一个测试将失败,直到您修复它。
我想说的主要观点是生产和测试代码具有不同的性质。然后它总是一个常识问题,我不是说你不应该考虑测试代码,等等。如果你可以在测试代码中考虑一些因素,你确定它没问题,那就去做吧。但是对于测试代码,我更喜欢简单而不是优雅,而对于生产代码,我更喜欢优雅而不是简单。最佳的当然是拥有简单,优雅的解决方案:)
PS:如果你真的不同意,请发表评论。
答案 2 :(得分:14)
测试代码与任何其他代码一样,应该进行维护和重构。
这意味着如果您有共享逻辑,请将其解压缩到自己的函数。
某些单元测试库(如xUnit系列)具有此类共享代码的特定测试夹具,设置和拆卸属性。
请参阅this相关问题 - “为什么复制代码粘贴会有危险?”。
答案 3 :(得分:8)
复制粘贴没有问题,这是一个很好的起点。事实上,它比从头开始更好,就好像你有工作代码(无论是测试还是其他),然后复制粘贴比从头开始更可靠,也更快。
然而,这只是第1步。第2步是重构共性,第1步只是为了帮助你看到这种共性。如果你已经可以在没有复制的情况下清楚地看到它(有时它更容易先复制再检查,有时它不是,而且取决于这样做的人),然后跳过第1步。
答案 4 :(得分:1)
如果您要重复代码,那么您必须重构。您的情况是一个常见问题,可以使用“参数测试”解决。测试工具支持时的参数测试允许将多组输入值作为参数传递。您可能还想查找Fuzz测试,我发现它在这种情况下很有用。
答案 5 :(得分:1)
请记住,您的测试正在推动您的代码。如果你发现你的测试看起来重复,除了像左/右这样的东西,那么也许有一些左右重复的底层代码。因此,您可能希望了解是否可以重构代码以使用左侧或右侧,并向其发送左侧或右侧标志。
答案 6 :(得分:1)
xunitpatterns.org网站说“不”(复制/粘贴不正常),因为在需要更新测试时会增加成本:
“剪切和粘贴”是一种用于快速编写代码的强大工具 导致相同代码的许多副本,每个副本必须是 保持平行。
并且为了进一步阅读它还链接到文章
作者:Arie van Deursen,Leon Moonen,Alex van den Bergh,Gerard Kok
答案 7 :(得分:1)
我同意@Rob。代码需要重构。但是如果您不想在此时重构代码,那么您可以进行参数化测试。针对不同参数运行相同的测试。请参阅nunit中的TestCase
和TestCaseSource
属性。
答案 8 :(得分:0)
我有时发现自己只是为了避免测试代码重复而进行非常复杂的单元测试。我认为这样做并不好。任何单个单元测试都应尽可能简单。如果你需要一个重复来实现它 - 让它成为。
另一方面,如果你的单元测试有+100500行代码,那么它显然应该被重构,这将是一个简化。
当然,尝试避免无意义的单元测试重复,例如测试1 + 1 = 2,2 + 2 = 4,3 + 3 = 6。如果您确实需要在不同的数据上测试相同的方法,请编写数据驱动的测试。