在某些组织中,显然,软件发布过程的一部分是使用单元测试,但是在任何时间点,所有单元测试都必须通过。例如,可能会有一些屏幕以绿色显示所有通过的单元测试-这应该是好的。

我个人认为,出于以下原因,这不是应该的:


它提出了这样的想法,即代码应该是完美的,并且不应该存在错误-在现实世界中,对于任何大小的程序,这肯定是不可能的。
思考单元是不利的测试将失败。或者肯定会提出很难修复的单元测试。
如果在任何时间点所有单元测试都通过了,那么在任何时间点软件状态都不会有大的概况。没有路线图/目标。
它阻止了在实施之前预先编写单元测试。

我什至建议即使发布失败的单元测试的软件也不一定是坏的。至少然后您知道该软件的某些方面有局限性。

我在这里缺少什么吗?为什么组织希望所有单元测试都能通过?这不是生活在梦想世界中吗?而且它实际上并不能阻止对代码的真正理解吗?

评论

评论不作进一步讨论;此对话已移至聊天。

#1 楼

这个问题包含恕我直言的几个误解,但我要重点关注的一个主要问题是,它不能区分本地开发分支,主干,登台或发布分支。

在本地dev分支中,几乎随时都有可能导致某些单元测试失败。在后备箱中,在某种程度上仅是可以接受的,但已经是一个强大的指标,可以尽快修复。请注意,在主干中失败的单元测试可能会干扰团队的其他成员,因为它们要求每个人都检查他/她的最新更改是否不是导致失败的原因。
在暂存或发布分支中,失败测试是“红色警报”,表明从主干合并到发布分支时,某些变更集出现了一些绝对错误。


我什至建议即使发布单元测试失败的软件不一定是不好的。


发布具有某些严重度低于一定严重性的错误的软件不一定是不好的。但是,这些已知的故障不应导致单元测试失败。否则,在每次单元测试运行之后,将不得不查看20个失败的单元测试,并逐一检查该失败是否可以接受。这变得麻烦,容易出错,并且丢弃了单元测试自动化方面的很大一部分。

如果您确实对可接受的已知错误进行了测试,请使用单元测试工具的禁用/忽略功能(因此您默认情况下不运行它们,而仅按需运行)。此外,向您的问题跟踪器添加低优先级票证,这样就不会忘记该问题。

评论


我认为这是真正的答案。 OP提到了“发布过程”和“某些屏幕[显示测试结果]”,这听起来像是构建服务器。发布与开发不同(请勿在生产中开发!);在开发人员中通过测试失败是很好的,就像TODO。当它们被推到构建服务器时,它们都应该是绿色的(完成)。

–Warbo
18年5月22日在14:08

比最高投票者的答案好得多。它显示了对操作源的了解,而没有向他们讲授一些理想的世界情况,承认了已知错误的可能性(并非为了解决某些罕见的极端情况而放弃了整个路线图),并解释了单元测试仅应明确在发布分支/进程中为绿色。

–塞巴斯蒂安·范·登·布鲁克(Sebastiaan van den Broek)
18年5月22日在16:54

@SebastiaanvandenBroek:感谢您的肯定答复。为了明确起见:恕我直言,失败的单元测试即使在后备箱中也很少见,因为这样的失败率太高会打扰整个团队,而不仅仅是造成失败的更改者。

–布朗博士
18年5月22日在17:56



我认为这里的问题是所有自动化测试都是单元测试。许多测试框架都具有标记预期失败的测试的能力(通常称为XFAIL)。 (这不同于需要错误结果的测试。理想情况下,XFAIL测试会成功,但不会成功。)测试套件仍会通过这些失败。最常见的用例是仅在某些平台上失败的事情(而在那些平台上只有XFAIL),但是使用该功能来跟踪目前需要过多工作才能修复的东西也是可以的。但是这些测试通常不是单元测试。

–凯文·卡斯卡特(Kevin Cathcart)
18年5月22日在23:04



+1,尽管我建议对这句话稍加(粗体):“这变得麻烦,容易出错,使人们忽略了测试套件中的故障(如噪音),并丢弃了单元测试自动化方面的很大一部分。

–mtraceur
18年5月25日在21:46

#2 楼


...所有单元测试都以绿色通过-这应该很好。


这很好。没有“理应如此”。


它提出了这样的想法,即代码应该是完美的,并且不应该存在错误-在现实世界中,对于任何大小的程序,这肯定是不可能的。


不。事实证明,到目前为止,您已经对代码进行了测试。
您的测试可能不会涵盖所有情况。如果是这样,最终将在错误报告中出现任何错误,您将编写[失败]测试以重现问题,然后修复应用程序以使测试通过。


考虑失败的单元测试是不利的。


失败或否定的测试对您的应用程序可以接受和不接受的内容设置了严格的限制。我知道的大多数程序都会反对2月30日的“日期”。
此外,开发人员(我们是创造性的类型)也不想破坏“他们的孩子”。结果导致对“幸福路径”案例的关注导致脆弱的应用程序经常中断。

要比较开发人员和测试人员的心态,请执行以下操作:


只要代码执行了他们想要的操作,开发人员就会停止。
当测试人员无法再使代码中断时,它就会停止。

这些是截然不同的观点,许多开发人员都难以调和。


当然也可以提出很难修复的单元测试。


您不必编写测试来自己动手。您编写测试以确保您的代码正在执行应做的事情,更重要的是,在更改其内部实现后,代码将继续执行应做的事情。


调试“证明”该代码可以满足您的要求。
测试“证明”该代码随着时间的推移仍然可以实现您想要的功能。


如果在任何时间点所有单元测试都通过了,那么在任何时间点都没有软件状态的大图。没有路线图/目标。


唯一的“图片”测试为您提供了一个快照,代码在测试的时间点“起作用”。在那之后它如何演变则是另外一个故事。


它阻止了在执行之前预先编写单元测试。


这正是您应该做的。编写一个失败的测试(因为尚未测试的方法尚未实现),然后编写方法代码以使该方法正常工作,并因此通过测试。这几乎是测试驱动开发的关键。


我什至建议即使发布单元测试失败的软件也不一定是坏事。至少然后您才知道该软件的某些方面有局限性。发布带有坏测试的代码意味着该功能的某些部分不再像以前那样起作用。这可能是有意为之,因为您已经修复了错误或增强了功能(但是随后您应该先更改测试以使其失败,然后对修复/增强进行编码,以使测试在此过程中进行)。更重要的是:我们都是人类,我们会犯错误。如果您破坏了代码,则应该破坏测试,而那些破坏的测试应设置警报铃声。


这不是生活在梦想世界中吗?


如果有的话,它生活在现实世界中,他们承认开发人员既无所不知也不犯错误,我们确实犯了错误,并且我们需要安全网来捕捉我们搞砸了!
输入测试。


并不能真正阻止对代码的真正理解吗?


也许。您不一定需要理解某些东西的实现来为其编写测试(这是它们的重点之一)。测试定义了应用程序的行为和限制,并确保除非您有意更改它们,否则它们将保持不变。

评论


@Tibos:禁用测试就像注释一个功能。您具有版本控制。用它。

–凯文
18年5月22日在15:26

@Kevin我不知道“使用它”的意思。我将测试标记为“跳过”或“待定”或测试运行程序使用的任何约定,并将该skip标签提交到版本控制中。

–装饰
18年5月22日在15:33

@dcorking:我的意思是不要注释掉代码,将其删除。如果以后决定需要它,请从版本控制中还原它。提交禁用的测试也没有什么不同。

–凯文
18年5月22日在16:14

“您的测试很可能不会涵盖所有情况。”到目前为止,我要说的是,对于经过测试的每一个平凡的代码,您肯定都不会涵盖所有情况。

– corsiKa
18年5月22日在16:24

@Tibos支持单元测试的人说,从编写失败的测试到为其编写代码的周期时间应该很小(例如20分钟,有些人声称30秒)。如果您没有时间立即编写代码,则可能太复杂了。如果不是很复杂,请删除测试,因为如果再次添加删除的功能,可以重写测试。为什么不注释掉呢?您不知道是否会再次添加该功能,因此注释掉的测试(或代码)只是杂讯。

– CJ Dennis
18年5月23日在2:49

#3 楼


为什么单元测试失败被认为是不好的?


它们不是-测试驱动的开发基于失败测试的概念。未能通过单元测试来推动开发,而未能通过验收测试来推动发展……。

你所缺少的是上下文;

通常的答案是,单元测试只能在私有沙箱中失败。

基本概念是:在环境中在共享失败的测试的地方,需要花费更多的精力来了解生产代码的更改是否引入了新的错误。零与非零之间的差异比N与非零之间的差异更容易检测和管理。

此外,保持共享代码的整洁意味着开发人员可以继续工作。当我合并您的代码时,我不需要将上下文从需要解决的问题上转移到校准对多少测试失败的理解。如果共享代码通过了所有测试,则当我合并更改时出现的任何故障都必须是我的代码与现有的干净基准之间进行交互的一部分。

类似地,在登机期间新开发人员可以更快地提高生产率,因为他们不需要花时间去发现哪些失败的测试是“可以接受的”。

更准确地说:纪律是在构建期间运行的测试必须通过。

,据我所知,禁用失败的测试没有错。

例如,在“连续集成”环境中,您将高度共享代码。经常进行集成并不一定意味着您的更改必须准备好发布。各种各样的黑暗部署技术可以防止流量在准备就绪之前释放到代码段中。

这些相同的技术也可以用于禁用失败的测试。

我在定点发布中进行的练习之一是处理具有许多失败测试的产品的开发。我们想到的答案只是浏览套件,禁用失败的测试并记录每个测试。这使我们能够快速达到所有启用的测试都通过的程度,并且管理层/目标捐赠者/金拥有者都可以看到我们为达到该点而进行了哪些交易,并且可以就清理与新工作做出明智的决定。

简而言之:除了将大量失败的测试留在运行套件中之外,还有其他技术可以用来跟踪未完成的工作。

评论


我会说“禁用失败的测试没有错”。

– CJ Dennis
18年5月23日在2:53

这一变化肯定澄清了含义。谢谢。

–VoiceOfUnreason
18年5月23日在12:30

#4 楼

有很多不错的答案,但是我想补充一个我认为尚未很好涵盖的角度:进行测试的确切意义。
没有单元测试来检查您的代码是否有错误免费。
我认为这是主要的误解。如果这是他们的职责,那么您确实希望到处都会有失败的测试。但是,相反,
单元测试会检查您的代码是否执行了您认为的工作。
在极端情况下,它可能包括检查已知错误是否已修复。关键是要控制您的代码库并避免意外更改。进行更改后,可以正常进行测试,并且可以预期会破坏某些测试-您正在更改代码的行为。现在,新近通过的测试可以很好地说明您所做的更改。检查所有破损是否都符合您的更改要求。如果是这样,只需更新测试并继续。如果不是,那么,您的新代码肯定是错误的,请在提交之前回去修复它!
现在,只有在所有测试均为绿色的情况下,以上所有方法才能正常工作,并给出强烈的肯定结果:这正是代码的工作方式。红色测试没有该属性。 “这是这段代码不做的”很少是有用的信息。
验收测试可能就是您想要的。
有验收测试之类的东西。您可以编写一组必须满足的测试才能调用下一个里程碑。这些可以是红色的,因为这是设计的目的。但是它们与单元测试有很大的不同,既不能也不应该替换它们。

评论


我曾经不得不用另一个替换图书馆。单元测试帮助我确保所有新案例仍然受到新代码的相同对待。

–索比昂·拉文·安德森(ThorbjørnRavn Andersen)
18年5月24日在20:12

#5 楼

我将其视为等同于破窗综合症的软件。

工作测试告诉我,该代码具有特定的质量,并且代码所有者在乎它。

关于何时应该关注质量,这取决于您正在使用的源代码分支/存储库。开发人员代码很可能有坏的测试指示正在进行的工作(希望如此!)。

对运行中的系统的分支/存储库中的损坏的测试应立即设置警报铃声。如果损坏的测试被允许继续失败或被永久标记为“忽略”,则期望它们的数量会随着时间的推移而增加。如果不定期检查这些内容,则将有先例可以保留残破的测试。

在许多商店中,破坏性测试被视为贬义词,以至于对是否可以提交破坏性代码产生了限制。

评论


如果测试记录了系统的运行方式,则它们肯定应该一直通过-如果没有通过,则意味着不变性已损坏。但是,如果它们记录了系统的预期方式,那么失败的测试也可以使用它们-只要您的单元测试框架支持将它们标记为“已知问题”的好方法,并且将它们与某个项目链接在您的问题跟踪器中。我认为这两种方法都有其优点。

–罗安
18年5月22日在13:02

@Luaan是的,这确实是假设所有单元测试都是一样创建的。对于构建管理者来说,根据测试的运行时间,脆弱程度以及其他各种条件,通过一些属性将测试切成薄片并切成小块,这种情况并不少见。

–罗比·迪(Robbie Dee)
18年5月22日在13:17

根据我自己的经验,这个答案很棒。一旦有人习惯于忽略一堆失败的测试,或在某些方面破坏最佳实践,请等待几个月,您将看到被忽略的测试百分比急剧增加,代码质量下降到“ hack-script”水平。而且很难使每个人都回想起这个过程。

–usr-local-ΕΨΗΕΛΩΝ
18年5月28日在10:15

#6 楼

这是潜在的逻辑谬误:


如果所有测试通过都很好,那么如果任何测试失败就一定是不好的。


对于单元测试,所有测试通过都很好。测试失败时,它也很好。两者不必相反。

测试失败是您的工具在到达用户之前就发现的问题。这是在错误发布之前纠正错误的机会。那是一件好事。

不好的是,允许失败的单元测试随着时间的流逝而持续,因为这会训练您忽略工具和报告。如果测试失败,是时候调查原因了。有时这是代码中的错误。有时这是测试中的错误。有时这是环境问题,或者假设已经改变。在某些情况下,问题有时是暂时的,但仍然会持续很长时间以至于令人困惑。在这种情况下,应禁用测试(在某处提醒您稍后再检查情况),直到问题可以解决。

评论


有趣的思路。我看到这个问题的谬误更像是这样:“因为单元测试失败会很好,所以所有测试通过都会不好”。

–布朗博士
18年5月22日在18:31

尽管您的最后一段很不错,但问题似乎是对“在任何时候所有单元测试都必须通过”(正如公认的答案所表明的)和单元测试点的误解。

– Bernhard Barker
18年5月23日在11:33



#7 楼

Phill W的答案很好。我无法替代它。

但是,我确实想专注于可能是造成混乱的另一部分。


在某些组织中,显然,软件发布过程的一部分是使用单元测试,但是在任何时间点所有单元测试必须通过


“在任何时间点”都夸大了您的案例。重要的是,在实施某个更改之后,在开始实施另一个更改之前,单元测试才能通过。
这是您跟踪哪个更改导致错误发生的方法。如果在实施变更25之后但在实施变更26之前单元测试开始失败,那么您就知道变更25引起了该错误。

在实施变更期间,单元测试当然会失败; tat很大程度上取决于变化有多大。如果我要重新开发一项核心功能,而不仅仅是一项细微的调整,那么我可能会中断测试一段时间,直到完成新版本的逻辑。


这可能导致团队规则发生冲突。我实际上是在几周前遇到的:


每次提交/推送都会导致构建。构建绝不能失败(如果失败或任何测试失败,则指责开发人员。)
每个开发人员都应在一天结束时推送他们的更改(即使未完成),因此团队负责人可以早上检查代码。

任何一条规则都可以。但是,这两个规则不能一起工作。如果我被分配了一项需要数天才能完成的重大更改,那么我将无法同时遵守这两个规则。除非我每天都对我的更改发表评论,并且仅在完成所有操作后才对它们进行注释。这只是荒谬的工作。

在这种情况下,这里的问题不是单元测试没有目的。这是公司抱有不切实际的期望。它们的任意规则集不能涵盖所有情况,并且不遵守规则被盲目地视为开发人员失败,而不是规则失败(在我的情况下就是这样)。

评论


一种可行的工作方式是使用分支,这样开发人员可以提交并推送到功能分支,这些分支不需要在不完整的情况下进行干净构建,但是提交到核心分支确实会触发构建,而构建应该进行干净的构建。

– Gwyn Evans
18年5月22日在17:57



强制推行不完整的更改是荒谬的,我看不出这样做的任何理由。为什么更改完成后不进行代码审查?

–卡勒姆·布拉德伯里(Callum Bradbury)
18年5月22日在21:49

好吧,对于一个人来说,这是一种快速的方法,可以确保不仅在开发人员的笔记本电脑/工作站上,如果他们的硬盘停止工作或以其他方式丢失代码,这些代码也可以保存在开发人员的笔记本电脑/工作站上-如果存在即使在工作过程中也要提交的策略,工作量有限。

– Gwyn Evans
18年5月22日22:00



功能标志修复了明显的悖论。

–RubberDuck
18年5月23日在10:07

@Flater是的,也可以用于修改现有逻辑。

–RubberDuck
18年5月23日在11:38

#8 楼

如果不修复所有单元测试,则可以迅速进入没有人修复任何损坏的测试的状态。


不正确,因为通过单元测试并不表明代码是完美的
提出同样难以测试的代码也是一种阻碍,从设计的角度来看,这是件好事。
代码覆盖率可以为您提供帮助(尽管这不是万能的)。同样,单元测试只是测试的一个方面-您也需要集成/验收测试。


#9 楼

要在已经很好的答案中添加一些要点...


,但是在任何时候,所有单元测试都必须通过


此表明缺乏对发布过程的了解。测试失败可能表明TDD下的计划功能尚未实现;或者它可能指示已知的问题,该问题已计划在将来的发行版中进行修复;或仅仅是管理层认为这并不重要,因为客户不太可能注意到这一点。所有这些共享的关键之处在于,管理层已经对失败做出了判断。


提出了这样的想法,即代码应该是完美的,并且不应该存在错误-在现实世界中对于任何大小的程序,这肯定是不可能的。


其他答案已经涵盖了测试的局限性。

我不明白为什么您认为消除错误是不利的一面。如果您不希望(已尽其所能)交付经过检查的代码,那么您为什么还要使用软件?


如果在所有单元测试在任何时间点都通过了,那么在任何时间点都没有软件状态的大图。没有路线图/目标。


为什么必须要有路线图?

单元测试最初会检查功能是否正常,但随后(作为回归测试)进行检查您没有无意间破坏了任何东西。对于现有单元测试的所有功能,没有路线图。已知每个功能都可以工作(在测试范围内)。如果该代码完成,则没有路线图,因为不需要进行更多工作。

作为专业工程师,我们需要避免镀金的陷阱。业余爱好者有能力浪费时间在有用的东西周围修修补补。作为专业人员,我们需要交付产品。这意味着我们可以使某项工作正常,验证其是否正常运行,然后继续进行下一项工作。

#10 楼


它提出了这样的想法,即代码应该是完美的,并且不应该存在错误-在现实世界中,对于任何大小的程序,这肯定是不可能的。


这不是事实。您为什么认为这不可能?以下是适用于该程序的示例:

public class MyProgram {
  public boolean alwaysTrue() {
    return true;
  }

  @Test
  public void testAlwaysTrue() {
    assert(alwaysTrue() == true);
  }
}



考虑失败的单元测试是不利的。或肯定会提出很难修复的单元测试。


在这种情况下,它可能不是单元测试,但如果很复杂,则是集成测试


如果在任何时间点所有单元测试都通过了,那么在任何时间点都没有软件状态的大图。没有路线图/目标。


是正确的,出于某种原因被称为单元测试,它检查一小段代码。






开发人员会阻止编写任何测试,如果他们不了解本质上的好处(除非他们来自QA)。 />

评论


“开发人员会根据其性质阻止编写任何测试” –完全是胡说八道。我在一家从事TDD和BDD开发人员的整个公司工作。

–RubberDuck
18年5月23日在10:09

@RubberDuck我试图回答一个有问题的“事实”,但我太夸张了。我会更新

–user7294900
18年5月23日在10:12

“如果X不理解Y的好处,就会阻止X进行Y”,这几乎适用于任何X和Y,因此该声明可能不是特别有用。解释编写测试的好处,尤其是提前进行,可能会更有意义。

– Bernhard Barker
18年5月23日在11:04



“任何大小的程序都不可能”并不表示“无论大小如何,所有程序”,而是“任何重要程序(长度不短)”,您尝试使用的反例是不适用的,因为它是“一个重要而有用的程序。

– Ben Voigt
18年5月25日22:00



@BenVoigt我不认为我会给出“重要程序”作为答案。

–user7294900
18年5月27日在12:53

#11 楼


提出了这样的想法,即代码应该是完美的,并且不应该存在错误。


绝对不是。它提出了这样的想法,即您的测试不应失败,仅此而已。假设进行测试(甚至很多测试)能说明“完美”或“无错误”的说法是谬论。确定测试应该是浅还是深是编写好的测试的重要组成部分,也是我们拥有不同测试类别的原因(“单元”测试,集成测试,“黄瓜”中的“方案”等)。 br />

考虑失败的单元测试是不利的。或者肯定会提出很难修复的单元测试。


在测试驱动的开发中,必须强制每个单元测试首先失败,然后再开始编码。出于这个原因,它被称为“红绿循环”(或“红绿重构循环”)。


在测试失败的情况下,您不知道该代码是否实际上已通过测试进行了测试。两者可能根本不相关。
通过更改代码以使测试从红色准确变为绿色,仅此而已,您可以确信自己的代码可以执行预期的工作,而不是更多(您可能永远不需要)。


如果在任何时间点所有单元测试都通过了,那么任何时候都不会对软件的状态有任何大的了解。时间点。没有路线图/目标。


测试更像是一个微型目标。在测试驱动的开发中,程序员将首先编写一个测试(单数),然后明确实现一些代码的目标。然后是下一个测试,依此类推。

在编写代码之前,测试的功能不完整。

如果正确完成操作,使用一种语言并且使用适合此方法的测试库,它实际上可以极大地加快开发速度,因为错误消息(异常/堆栈跟踪)可以直接将开发人员指向他需要执行工作的位置下一个。


它阻止了在执行之前预先编写单元测试。


我不知道这句话是怎么回事。理想情况下,编写测试应该是实现的一部分。


我在这里缺少什么吗?为什么组织希望所有单元测试都能通过?


因为组织期望测试与代码相关。编写成功的测试意味着您已经记录了应用程序的某些部分,并证明了该应用程序能够完成(测试)所说的操作。一无所有。

此外,进行测试的很大一部分是“回归”。您希望能够自信地开发或重构新代码。进行大量的绿色测试可以使您做到这一点。

从组织到心理层面。知道自己的错误很有可能会被测试发现的开发人员将更加自由地为自己需要解决的问题提供智能,大胆的解决方案。另一方面,没有测试的开发人员将在一段时间后陷入僵局(由于恐惧),因为他不知道自己所做的更改是否会破坏应用程序的其余部分。


这不是生活在梦想世界中吗?


不。使用测试驱动的应用程序纯属喜悦-除非您出于某种原因(例如“更多的努力”等)不喜欢该概念,我们可以在另一个问题中进行讨论。


并不能真正阻止对代码的真正理解?


绝对不是,为什么会这样?

您会发现大量大型开源项目(对于这些项目而言,“理解”和代码专有技术的管理是非常紧迫的主题),除了测试之外,这些项目实际上将测试用作软件的主要文档,还为应用程序/库的用户或开发人员提供了在语法上正确,真实,可行的示例。这通常很出色。

显然,编写不好的测试是不好的。但这与测试本身的功能无关。

#12 楼

(根据我的原始评论)

所需的功能和未来的目标之间存在差异。测试是针对必需功能的:它们是精确,正式,可执行的,并且如果失败,则该软件将无法正常工作。未来的目标可能不是精确或正式的,更不用说可执行了,因此最好使用自然语言,例如问题/错误跟踪器,文档,评论等。

作为练习,请尝试替换您的问题中带有“编译器错误”(或“语法错误”,如果没有编译器)的短语“单元测试”。显然,发行版不应该包含编译器错误,因为它将无法使用。但是,编译器错误和语法错误是开发人员在编写代码时的正常状态。错误只有在完成后才会消失。这正是应该推送代码的时间。现在,将本段中的“编译器错误”替换为“单元测试” :)

#13 楼

自动化测试的目的是告诉您何时尽早破坏了某些东西。工作流程如下所示:


进行更改
(理想情况下自动构建并测试您的更改)
如果测试失败,则表明您已失败以前有效的方法
如果测试通过,您应该确信所做的更改不会引入新的回归(取决于测试覆盖率)

如果测试已经失败,则步骤3不会可以有效地工作-测试将失败,但是您不知道那是否意味着您不进行调查就破坏了某些东西。也许您可以计算失败测试的次数,但是更改可能会修复一个错误而又破坏另一个错误,或者测试可能由于其他原因而开始失败。这意味着您需要等待一段时间才能知道是否已损坏,直到解决所有问题或对每个失败的测试进行了调查。

单元测试的能力尽早发现新引入的错误是自动化测试中最有价值的事情-缺陷被发现的时间越长,修复它的成本就越高。


它促进了代码的想法应该是完美的,并且不应该存在错误。
考虑单元测试会失败



测试无效的测试不会告诉您任何事情-为有效或即将解决的事情编写单元测试。这并不意味着您的软件没有缺陷,而是意味着您先前编写的单元测试的所有缺陷都不会再次出现。


它阻止了预先编写单元测试


如果它对您有用,那么请先编写测试,直到它们通过后再将它们检入您的主/主干中。


如果在任何时间点所有单元测试都通过了,那么在任何时间点都没有软件状态的大图。没有路线图/目标。


单元测试不是用来设置路线图/目标的,也许可以使用积压的?如果您所有的测试都通过了,那么“大局”就是您的软件没有损坏(如果您的测试范围很好)。干得好!

#14 楼

现有的答案当然是好的,但是我还没有看到有人在以下问题上解决过这个基本的误解:


在任何时间点所有单元测试都必须通过


不。无疑,这不是事实。在我开发软件时,NCrunch通常要么是棕色(构建失败)要么是红色(测试失败)。

需要在我准备推送一个绿色的NCrunch(所有测试通过)的地方提交到源代码管理服务器,因为那时其他人可能会依赖我的代码。

这也涉及创建新测试的主题:测试应声明代码的逻辑和行为。边界条件,故障条件等。在编写新测试时,我会尝试在代码中识别这些“热点”。

单元测试记录了我如何期望代码被调用-前提条件,预期条件输出等。

如果更改后测试失败,则需要确定代码或测试是否错误。


作为旁注,单元测试有时会与“测试驱动开发”并驾齐驱。 TDD的原则之一是坏的测试是您的指南。如果测试失败,则需要修复代码以使测试通过。这是本周早些时候的一个具体示例:

背景:我编写并现在支持我们的开发人员使用的用于验证Oracle查询的库。我们进行的测试断言该查询符合某个期望值,这使得情况变得很重要(在Oracle中不重要),并且只要无效查询完全符合期望值,就可以适当地批准无效查询。

相反,我的库使用Antlr和Oracle 12c语法解析查询,然后在语法树本身上包装各种断言。诸如此类,它是有效的(没有引发解析错误),其所有参数均由参数集合满足,数据读取器读取的所有预期列均出现在查询中,等等。所有这些都是已转至

我的一位同事在星期一给我发送了一个查询,该查询在周末失败了(或者应该在应该失败的情况下成功了)。我的图书馆说语法不错,但是在服务器尝试运行它时就爆了。当他查看查询时,很明显的原因:

UPDATE my_table(
SET column_1 = 'MyValue'
WHERE id_column = 123;


我加载了项目并添加了一个单元测试,该测试断言该查询无效。显然,测试失败了。

接下来,我调试了失败的测试,逐步浏览了我期望它引发异常的代码,并发现Antlr在打开的paren上引发了错误,不像以前的代码所期望的那样。我修改了代码,确认测试现在是绿色的(通过),并且没有其他人在该过程中出现问题,已提交并已推送。

这大概花费了20分钟,在此过程中,我实际上进行了重大改进,因为该库现在支持以前被忽略的全部错误。如果我没有对该库进行单元测试,那么研究和解决该问题可能要花几个小时。

#15 楼

“但是任何时候任何单元测试都必须通过”。

如果这是贵公司的态度,那就是问题。在某个特定时间,即当我们声明代码已准备好移至下一个环境时,所有单元测试都应通过。但是在开发过程中,我们通常应该期望许多单元测试会失败。

没有一个合理的人期望程序员在第一次尝试时就能使自己的工作变得完美。我们可以合理预期的是,他将继续努力直到没有已知问题为止。

“想出会失败的单元测试是一种抑制作用。或者肯定会提出可以避免的单元测试。修复起来会很棘手。”如果组织中的某人认为他们不应该提及可能的测试,因为它可能会失败并导致他们进行更多的工作以进行修复,则该人完全没有资格胜任其工作。这是一种灾难性的态度。您是否想请一位医生说:“当我做手术时,我故意不检查针迹是否正确,因为如果发现针迹不正确,我将不得不回去重新做一遍,会减慢操作的完成速度吗?”

如果团队对在代码投入生产之前发现错误的程序员怀有敌意,那么您对该团队的态度就会产生真正的问题。如果管理人员惩罚那些发现错误并降低交付速度的程序员,那么很可能您的公司就要破产了。

是的,有时候理性的人说:“我们快到了最后期限,这是一个微不足道的问题,现在不值得花费资源来修复它。”但是,如果您不知道,就无法理性地做出决定。冷静地检查错误列表并分配优先级和时间表以修复它们是合理的。故意使自己对问题一无所知,所以您不必做出此决定是愚蠢的。您是否认为客户会因为您不想知道而无法找到?

#16 楼

我不认为从先前的答案中得出的一点是,内部测试和外部测试之间存在差异(而且我认为许多项目对区分这两者的注意不够)。内部测试测试某些内部组件是否按预期方式工作;外部测试表明,该系统总体上按预期运行。当然,组件中的故障很可能不会导致系统故障(也许某个组件的功能未被系统使用,或者系统从故障中恢复了)。零件)。不会导致系统故障的组件故障不应阻止您释放。

我看到项目因内部组件测试过多而瘫痪。每次尝试实现性能改进时,都会破坏数十个测试,因为要更改组件的行为而不实际更改系统的外部可见行为。这导致整个项目缺乏敏捷性。我相信,与对内部组件测试的投资相比,对外部系统测试的投资通常具有更好的收益,尤其是当您谈论的是非常低级的组件时。

当您认为失败的单元测试并不重要时,我想知道这是否是您要考虑的?也许您应该评估单元测试的价值,并摒弃那些引起更多麻烦的单元测试,同时将更多的精力放在验证应用程序外部可见行为的测试上。

评论


我认为您所说的“外部测试”通常在其他地方称为“集成”测试。

– GalacticCowboy
18年5月24日在14:22

是的,但是我遇到过术语上的差异。对于某些人来说,集成测试更多地是关于已部署的软件/硬件/网络配置,而我所说的是您正在开发的软件的外部行为。

–麦凯(Michael Kay)
18年5月24日15:16

#17 楼

这是确认偏差的一个具体例子,人们倾向于寻找可以确认其现有信念的信息。

2,4,6游戏就是这种情况的一个著名例子。


我头脑里有一个规则,任何三个数字序列都会通过或失败,
2,4,6是通过
您可以列出三个数字的集合,我会告诉你他们是通过还是失败。

大多数人会选择一条规则,说“第一和第二个数字之间的差距与第二和第三个之间的差距相同。”

他们将测试一些数字:


4、8、12?通过
20、40、60?通过
2,1004,2006?通过

他们说:“是的,每一次观察都证实了我的假设,这必须是真的。”并向发出谜语的人宣布他们的规则。

但是,他们从来没有收到过三个数字中任何一个的“失败”。对于它们实际拥有的所有信息,规则可能只是“三个数字必须是数字”。

规则实际上只是数字按升序排列。人们通常只有在测试失败时才能正确理解这个谜语。大多数人通过选择一个更具体的规则并仅测试满足此特定规则的数字来弄错这一点。

关于为什么人们会陷入确认偏见的情况,并且可能会将单元测试视为失败的证据问题,有很多心理学家可以比我更好地解释确认偏见,这基本上归结为人们不喜欢错了,而努力去真正地证明自己错了。

评论


它与问题有什么关系?根据定义,单元测试失败是问题的证据。

–弗拉克斯
18年5月23日在8:52

您绝对可以进行要求被测系统进入故障模式的单元测试。这与从未看到测试失败不一样。这也是为什么将TDD指定为“红色->绿色->重构”周期的原因

– Caleth
18年5月25日在12:38