我应该如何测试我的测试代码?


22

大多数软件开发人员都同意的几件事之一是,除非您对其进行测试,否则您不应依赖于代码才能正常工作。如果您不对其进行测试,则可能包含隐藏的错误,这些错误只会导致您日后要做更多的工作。

我知道如何测试我的普通代码,但是我应该如何测试我的测试代码,以确保它们可以有效地发现并报告出现的错误?我个人非常愚蠢,无法编写本应通过的错误测试用例,因此一开始就破坏了我编写测试的目的。幸运的是,我及时发现并修复了错误,但是根据测试的口头禅,如果没有一套自己的测试来确保它能够正常工作,似乎没有一套完整的测试套件。

在我看来,执行此操作的最佳方法是确保错误代码的测试失败。*如果我花2分钟的时间交替向代码中添加错误并确保失败,那么我应该有一个可以接受的信心,即测试“工作”。这使我想到第二个问题:有什么好的方法可以引入bug以确保它们被测试用例捕获?我是否应该随机注释掉语句,if-else否定条件来确保运行错误的分支,并更改带有副作用的代码的执行顺序等,直到我满意我的测试将抓住大多数常见的错误?专业开发人员如何验证他们的测试确实按照他们的预期去做?他们只是假设测试有效,还是花时间测试它们?如果是这样,他们如何测试测试?

我并不是在建议人们花太多时间测试他们的测试,然后再测试他们的测试,以至于他们从来没有真正编写过真正的代码,但是我做的愚蠢的事情让我觉得我可以从中受益进行“元测试”,并且对实现此目标的最佳方法感到好奇。:D

*我可以检查在测试“无错误”代码时测试是否通过,但是使用代码作为测试规范似乎倒退了……


听起来您对单元测试没有信心-很可能是因为您缺乏编写测试的经验?在这种情况下,编写更多的测试并期望获得不同的结果将是不合理的。只需继续做您要做的事情,就尽可能做到周全(测试失败和成功),然后您的单元测试将开始收回成本-您对它们的信心将会增强。
MattDavey


在使用更多工具之前,请更好地使用实际工具。编写较少的测试,但编写更有效,更好的测试。您不能相信自己不了解的东西。
Steve Chamaillard

Answers:


16

TDD的标准流程为:

  1. 编写失败的测试。(红色)
  2. 进行最小的更改以使其通过(绿色)
  3. 重构(保持绿色)

在这种情况下,测试的测试是步骤1-在进行任何代码更改之前,请确保测试失败。

我喜欢的另一个测试是您是否可以删除某些代码并以不同的方式重新实现它,并且删除后测试会失败,但是可以使用其他算法。

与所有事物一样,没有神奇的子弹。对于开发人员而言,忘记编写所需的测试就像忘记编写代码一样容易。至少如果您同时做这两项,那么发现遗漏的机会就会增加两倍。


4
双重记账:单元测试测试生产代码,反之亦然。它们是陈述同一问题的两种不同方式。就像在双重簿记中一样,您以两种不同的方式记录交易,并且两者都必须获得相同的总数。
EricSchaefer

问题是,即使代码仍然有错误,第2步使测试变为绿色时仍存在问题。一个简单的例子:您有一个函数,该函数必须返回一个指向非空列表对象的指针。您的测试将null在第1步测试指针是否存在并失败。通过返回一个空列表,对代码进行最小的更改以使其通过。该测试现在为“绿色”,因为您有两个错误。
Christian Hackl

@ChristianHackl在那个开发阶段是一个完美的实现!您必须添加一个(或多个)首先失败的测试,以进一步指定预期的行为。随后,您将其实现,使这些测试变为绿色。
安迪

@Andy:当我在代码和测试中都有错误,并且一个使我无法注意到另一个时,请仔细说明这是一个完美的实现吗?(代码中的错误是返回了一个空列表,而测试中的错误是我检查了null而不是空。)
Christian Hackl

@ChristianHackl您根据空支票是对的。确实,这也应该在测试中完成。当您将需求逐步转换为测试时,几乎没有bug的余地。除了您不遵守规范的情况(例如忽略非空支票)。
安迪

9

一种方法是使用Jester之类的工具进行突变测试

杰斯特(Jester)对您的代码进行了一些更改,运行您的测试,如果测试通过,杰斯特(Jester)会显示一条消息,说明已更改的内容


4

测试测试?不要走那条路。然后,您可能需要针对测试进行测试,然后针对测试进行测试……您在哪里停下来?

通常的测试流程是这样的,作为开发人员,您将大部分时间花在1-3点上:

  1. 单元测试
  2. 整合测试
  3. 系统/其他自动化
  4. 质量检查/测试人员

如果我花2分钟的时间交替向代码中添加错误(...)

您的代码最终将“增长”自己的错误,不要浪费时间手工介绍它们。更不用说,您对前期了解的确实是一个错误吗?虫子会来的,我不用担心。

如果我只是随机注释掉语句,请通过否定条件来确保if-else的错误分支运行(...)

这实际上是一种可行的方法,可以验证您是否实际测试了自己的想法。我不认为它总是那么好,因为它遇到的问题与“测试以测试……”相同:什么时候您停止更改代码,而知道要测试的代码100%有效?

记住所有经典的实用程序员建议也很有趣 - 您将不需要它。敏捷,为实际的错误编写测试和代码,而不是为那些可能出现或可能不会出现的假设编写代码。


3
我不担心我的代码会产生自己的错误,我担心我的测试会在它们发生时捕获它们。如果我的测试有错误,它们将无法完成工作,我认为当它们仍然存在时,我已经摆脱了某种错误,只会使我的工作更加困难,因为我正在查看错误的测试结果(通过它们应该失败时通过)。
Gordon Gustafson

@CrazyJugglerDrummer:您的测试不会捕获所有肯定的错误。他们没有达到这个目的-这就是QA的目的。如果这样做,那就意味着软件是没有错误的,除非源代码发生了变化(这是我从未见过的)。
公里

3

通过构造,功能代码和测试代码彼此进行了测试。问题仍然存在:共模错误,即功能代码中的错误被测试代码中的错误所掩盖。TDD不能幸免于此。这就是为什么通常由不同的人在多个级别进行测试以降低这种可能性的原因。


0

您可以在调试器中对单元测试进行一次测试。然后,您就不理会它了。这里没有问题。

考虑一下。单元测试的目的是什么?当您将在主程序中进行的众多更改中的任何一项意外更改该程序中的逻辑时,它都会通知您。您想拥有它,因为您知道任何更改都可能会破坏某些东西。这就是为什么如果不测试的话就没有问题的原因:只有故意更改程序的逻辑(这需要您重新访问测试并再次测试),才可以搞定测试。测试不太可能意外中断。


-2

一个变异测试可以评估和衡量测试的适用性和质量。

我们可以使用它来评估“测试”本身。

简而言之,我们可以通过测试TestA来发现代码及其突变代码之间的差异(与原始代码非常相似,但略有不同)来评估我们的测试(例如TestA)。

如果TestA找不到该代码及其突变代码之间的差异,则表明TestA的法规太粗糙,无法测试原始代码。

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.