复制并粘贴测试代码:这有多糟糕?


12

我目前的工作主要是为我们正在处理的各种应用程序编写GUI测试代码。但是,我发现我倾向于在测试中复制和粘贴很多代码。原因是我正在测试的区域趋于足够相似以至于需要重复,但还不够相似以至于无法将代码封装到方法或对象中。我发现,当我尝试更广泛地使用类或方法时,测试变得更加笨重,有时甚至一开始就很难编写。

取而代之的是,我通常从一个部分复制大量测试代码并将其粘贴到另一部分,然后进行我需要的任何细微更改。我不使用更结构化的编码方式,例如使用更多的OO原理或函数。

其他编码人员在编写测试代码时有这种感觉吗?显然,我想遵循DRY和YAGNI原则,但是我发现测试代码(无论如何都用于GUI测试的自动测试代码)会使这些原则难以遵循。还是我只需要更多的编码实践和更好的整体服务系统?

编辑:我正在使用的工具是SilkTest,这是一种称为4Test的专有语言。同样,这些测试主要针对Windows桌面应用程序,但是我也使用此设置对Web应用程序进行了测试。


您正在使用什么测试工具?可能是您的测试框架不支持您正在编写的测试类型。通常,剪切粘贴超过3行确实很糟糕,但是如果您能够通过自动化GUI测试明显地增加更多的长期价值,而不是每次都手动执行,那么您所做的任何事情都可能是很精明的好。
GlenPeterson 2012年

还有,这是什么语言?您可能会想到一些可用的东西,这些东西将允许重用(例如一流的函数)。在另一方面,测试用例应该保持简单,以保持它不太可能他们有自己的错误...
Izkata

3
在什么我已经写了,测试代码不从重构排除..
西蒙·怀特黑德

Answers:


23

复制粘贴然后再编辑的测试用例通常很好。

测试应具有尽可能少的外部依赖关系,并且应尽可能简单明了。测试用例倾向于随时间变化,并且以前几乎相同的测试用例可能会突然出现分歧。更新一个测试用例而不必担心破坏其他用例是一件好事。

当然,可以并且应该排除在许多测试案例中相同且必须一致更改的样板代码。


1
这主要是我的感觉。在许多情况下,几乎相同的测试代码是可以的,但是重复相同的测试代码是个坏消息。
joshin4colours 2012年

12

重复是万恶之源

没错!重复是万恶之源。也许是努斯在他的书中说“过早的优化是万恶之源”,但我认为这是重复。

每当您查看某个程序或编写一个程序时,就会发现某种重复:删除它!立即杀死它…… 除掉它

每次我引入某种重复并不得不修复其中的错误时,我都忘了修复副本...(Donald Knuth)因此,只要有重复,就尽可能地将其删除,不要乱砍!

考虑一个干净的精益设计(例如,将重复的代码块封装在帮助器类中),并在更改某些内容之前编写一些测试(以确保您没有破坏某些内容)。对于编写的任何代码都是如此,测试代码也不例外。

这是《 Code Horror》的一本好书,这启发了我- 对Code Reuse的复制粘贴学校提出的谦虚建议


“每次我引入某种重复并不得不修复其中的错误时,我都忘记了修复副本……”。另外,如果您选择了c&p,而忘记将复制的文本调整为当前上下文,这会带来很多伤害。错误的测试代码听起来不像是最佳情况,现在可以了吗?
marktani 2012年

是的,我从克努斯(Knuth)出发了:)
尤苏波夫

9
您重复了自己:在标题和简介句子中说“重复是万恶之源”,从而重复了自己。
Thomas Eding 2012年

叶氏,我没有故意要强调的重要性,并欢迎您来这就是编辑:)部分
Yusubov

1
托马斯·爱丁(Thomas Eding),你也重复了自己。您也重复了自己=)
marktani 2012年

7

剪切和粘贴仍然很糟糕。有一些问题。

您的测试可能很脆弱,因为您很容易受到需要更改所有复制粘贴代码的影响。您是否需要重写所有测试?

如果无法在测试之外将逻辑封装到帮助器方法中,则不能编写这些帮助器方法本身的测试。通常很难编写测试方法的测试,因为您必须破坏代码来测试该测试。但是您可以对辅助方法进行单元测试。

这很可能会使测试的可读性降低。与使用描述性名称调用辅助方法相比,一大段复制的代码可能更难阅读。

我列出的所有内容都可能有问题。如果您发现它们实际上都不是问题,那当然很好。


>使用描述性名称的辅助方法调用。这不是问题,因为您的单元测试现在已经变成了自己的程序,这是要避免的。如果某些测试失败了该怎么办-是代码损坏了,还是测试助手?
dwjohnston '16

4

我曾经同意你的看法。但是后来,随着时间的流逝,我发现我所做的每项更改(尤其是单元测试中的DI更改)都需要更改大量测试,而且很麻烦。现在,即使在编写测试时,我也加入了DRY学校。

对于GUI测试,您可能希望查看PageObject模式以减少重复的代码。


2

我建议选择XUnit模式。在开始利用那本书之前,我曾经遇到过完全相同的问题。听起来“ 对象母亲”模式对您的方案将是最有用的。

就像其他人提到的那样,正确封装此设置代码可能很麻烦,但必须在复制和粘贴的所有位置进行更改,甚至更麻烦。


Object Mother pattern通用初始化代码的+1 。
2012年

2

人们应该在可能的情况下尝试限制重新分配-是的。但是回报取决于情况。这可以追溯到“最佳实践”辩论。但是问题是,在这种情况下最适合您的是什么。每个规则都有例外。

我要问的几件事是:1)在UAT中测试的此功能有多大可能会改变?如果不太可能更改,那么您几乎不必更新每个代码集。2)如果UAT有变化,它会始终影响复制的代码的每一组还是仅影响一两套?如果它可能是孤立的,而只需要更改一组,则可能有助于将事物分离。3)如果尝试并处理所有方案,初始方法将有多复杂?您是否添加了很多嵌套的if / else / loops?如果您开始过度执行所有分支,则最终可能会得到难以理解的代码。在每个复制的文本中进行更新是否比重新访问所有分支逻辑更容易?

如果您卡住了复制/粘贴/更改,我想您想添加注释,例如“在方法xyz中复制”。这样,将提醒您更新所有粘贴的代码版本。或者(来自另一个SilkTest用户)可以添加一个单独的inc文件,该文件仅专注于此重复代码。这样一来,您就可以将所有变体放在一个地方,并且可以轻松地看到需要更新的不同方法。


0

一个大程序

一个想法:听起来您正在通过以下方法来尝试避免剪切粘贴代码:

testScreen(title, fieldList, linkList, param1, param2, param3,...) {
    test that the layout at the top of the screen is correct
    test if PageTitle == title?
    for each field in fieldList:
        check that it appears in order on the screen
    for each field in linkList:
        check that it appears in order on the screen
    test if param1 is whatever...
    test if param2 is whatever...
    etc.
    test that the bottom of the screen is correct
}

许多小程序(工具包)

您是否也考虑过相反的方法?与其将一百万个参数传递给一个大的testScreen()过程,不如制作自己的框架或由小助手程序组成的工具包,并在需要时将其淘汰掉。喜欢:

testScreenTop()
verifyLinks(list)
testScreenBottom()

您仍将这些过程剪切并粘贴到每个屏幕中,但是您正在剪切并粘贴较小的代码块,并切出未剪切和粘贴的通用性块(每个小过程的内容)。

剪切和粘贴

剪切粘贴的代码唯一没有被我咬过的时间是在不得不更改代码之前将其扔掉的时候。我对UI测试最大的担忧是它们过时的速度。如果发现您在更改代码之前就将所有代码都扔掉了,那么也许您已经找到了可以粘贴和粘贴的利基市场!而且,当剪切和粘贴的代码下游没有代码时(例如,在应用程序的UI中),这还不错。如果您粘贴的行数超过3行,那么我真的会考虑做一些事情。至少要采取步骤将其最小化!

自动化的UI测试

哎呀,如果您能证明自动化UI测试的生产力要比使用任何技术的手动测试更高(编写/维护自动化测试的成本低于每次手动测试的成本,但质量相同),我认为您应该写一篇论文。我会读的!我现在可以看到标题“为UI测试粘贴并粘贴代码一个网络胜利!”


0

确实还不错。实际上,如果您发现某些代码模式被经常使用并且更改非常常规(例如一些字符串或参数值),则您甚至可以编写一个代码生成器,该代码生成器会基于一个小的(- ?)输入要更改的值的列表。我已经使用批处理文件,SQLPlus脚本甚至Excel宏(听起来很丑,但是用于不同测试脚本的变量已经在电子表格中)做了很多次(生成测试代码),这可以节省很多时间。问题是,如果重复性测试用例代码的整体结构发生任何变化,您就可以重新生成所需的任何内容。


0

这与大多数其他答案相同,但以非技术经理可以理解的方式。

想象以下错误情况:

  • 有人更改了许多测试所依赖的数据库表。
  • 结果:2933个自动测试中突然有117个失败。

你会怎么做?

  • (1)修复117个测试?
  • (2)删除117个测试,然后通过新的复制和粘贴重新实现它们。这可能比(1)容易
  • (3)重构测试以提取通用代码,以便将来只需要采用一种(或几种)方法来修复测试(请参阅@pdr或@Michael Brown的答案)
  • (4)在不重新执行测试的情况下删除117个测试

根据我的经验:

引入自动化测试管理时,例如“复制并粘贴测试”:您可以在短时间内获得许多测试。

在某些“错误情况”之后,管理人员倾向于(4),因为修复“复制和粘贴测试”非常昂贵。

首先正确地做(3)不会那么快,但是会增加测试存活的机会

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.