7

我主要花时间在 win32 和 .NET 应用程序的自动化测试上,这需要我们大约 30% 的时间来编写和 70% 的维护。我们一直在寻找减少维护时间的方法,并且已经转移到一个可重用的测试库,该库涵盖了我们软件的大部分关键组件。此外,我们正在进行一些工作,以使我们的库达到可以使用基于关键字的测试的状态。

我一直在考虑对我们的测试库进行单元测试,但我想知道这是否值得花时间。我是软件单元测试的坚定支持者,但我不确定如何处理测试代码。

你认为自动化的 Gui 测试库应该进行单元测试吗?还是只是浪费时间?

4

13 回答 13

11

首先,我发现将单元测试视为“可执行规范”而不是测试非常有用。我写下我希望我的代码做什么,然后实现它。我从编写单元测试中获得的大部分好处是它们推动了实现过程并集中了我的思考。它们可重复用于测试我的代码这一事实几乎是一个令人愉快的巧合。

测试测试似乎只是解决问题而不是解决问题的一种方式。谁来测试测试测试的测试?TDD 用来确保测试真正有用的“技巧”是让它们首先失败。这可能也是您可以在这里使用的东西。编写测试,看到它失败,然后修复代码。

于 2009-02-03T13:52:09.637 回答
9

我不认为你应该对你的单元测试进行单元测试。

但是,如果您已经编写了自己的测试库,带有自定义断言、键盘控制器、按钮测试器或其他任何东西,那么可以。您应该编写单元测试来验证它们是否都按预期工作。

例如,NUnit 库经过单元测试。

于 2009-02-03T13:47:08.443 回答
5

从理论上讲,它软件,因此应该进行单元测试。如果您正在滚动自己的单元测试库,特别是,您将希望随时对其进行单元测试。

但是,您的主要软件系统的实际单元测试永远不会增长到足以需要单元测试的程度。如果它们太复杂以至于需要进行单元测试,那么您需要对软件进行一些认真的重构,并注意简化单元测试。

于 2009-02-03T13:53:08.790 回答
5

您可能想看看谁测试了测试

简短的回答是代码测试测试,测试测试代码。

嗯?

测试原子钟
让我从一个类比开始。假设你带着原子钟旅行。你怎么知道时钟校准正确?

一种方法是向您的邻居询问原子钟(因为每个人都随身携带一个)并比较两者。如果他们同时报告,那么你有很高的信心他们都是正确的。

如果它们不同,那么您就知道其中一个是错误的。

所以在这种情况下,如果你问的唯一问题是“我的时钟给出的时间是否正确?”那么你真的需要第三个时钟来测试第二个时钟和第四个时钟来测试第三个吗?如果全部不是。避免堆栈溢出!

IMPO:这是你有多少时间和你想拥有多少质量之间的权衡。

  • 如果我要使用自制的测试仪,如果时间允许,我会测试它。
  • 如果它是我使用的第三方工具,我希望供应商已经对其进行了测试。
于 2009-02-03T13:53:31.910 回答
2

确实没有理由可以/不应该对您的库进行单元测试。有些部分可能太难正确地进行单元测试,但其中大部分可能可以毫无问题地进行单元测试。

对这种代码进行单元测试实际上可能特别有益,因为您希望它既可靠又可重用。

于 2009-02-03T13:44:55.453 回答
2

测试测试代码,代码测试测试。当您以两种不同的方式(一次在测试中,一次在代码中)说出相同的意图时,它们都出错的可能性非常低(除非需求已经错误)。这可以与会计师使用的双重记账法相提并论。见http://butunclebob.com/ArticleS.UncleBob.TheSensitivityProblem

最近在http://blog.objectmentor.com/articles/2009/01/31/quality-doesnt-matter-that-much-jeff-and-joel的评论中讨论了同样的问题


关于您的问题,是否应该测试 GUI 测试库...如果我理解正确,您正在制作自己的测试库,并且您想知道是否应该测试您的测试库。是的。为了能够依赖库正确报告测试,您应该进行测试以确保库不报告任何误报或误报。不管测试是单元测试、集成测试还是验收测试,至少应该有一些测试。

通常在编写完代码之后再编写单元测试为时已晚,因为那样代码往往会更加耦合。单元测试迫使代码更加解耦,因为否则小单元(一个类或一组密切相关的类)无法单独测试。

当代码已经写好时,通常你可以只添加集成测试和验收测试。它们将在整个系统运行的情况下运行,因此您可以确保功能正常工作,但覆盖每个极端情况和执行路径比单元测试更难。

于 2009-02-04T11:13:41.217 回答
2

Kent Beck 的“测试驱动开发:通过示例”一书中有一个单元测试框架的测试驱动开发示例,因此当然可以测试您的测试。

我没有使用过 GUI 或 .NET,但是您对单元测试有什么顾虑?

您是否担心它在正常运行时可能会将目标代码描述为不正确?我想这是一种可能性,但如果发生这种情况,您可能能够检测到。

或者您是否担心它可能将目标代码描述为正常运行,即使它不是?如果您对此感到担心,那么突变测试可能就是您所追求的。变异测试会更改正在测试的部分代码,以查看这些更改是否会导致任何测试失败。如果没有,那么要么代码没有运行,要么代码的结果没有被测试。

如果您的系统上没有可用的突变测试软件,那么您可以手动进行突变,方法是自己破坏目标代码并查看它是否会导致单元测试失败。

如果您正在构建一套不依赖于特定应用程序的单元测试产品,那么也许您应该构建一个可以在其上运行测试软件并确保它获得预期的失败和成功的简单应用程序。

突变测试的一个问题是它不能确保测试涵盖程序可能遇到的所有潜在场景。相反,它只确保目标代码预期的场景都经过测试。

于 2009-02-11T02:50:13.200 回答
2

我们通常使用这些经验法则:

1) 所有产品代码都有单元测试(与产品代码类和功能紧密对应)和单独的功能测试(按用户可见的特性排列)

2) 不要为 .NET 控件或第三方库等第三方代码编写测试。例外情况是,如果您知道它们包含您正在解决的错误。对此的回归测试(当 3rd 方错误消失时失败)将在升级到 3rd 方库修复错误时提醒您,这意味着您可以删除您的解决方法。

3) 单元测试和功能测试本身并没有被直接测试过——除了使用 TDD 程序在产品代码之前编写测试,然后运行测试以观察它失败。如果你不这样做,你会惊讶于意外编写总是通过的测试是多么容易。理想情况下,您将一次执行您的产品代码,并在每次更改后运行测试,以便查看测试中的每个断言都失败,然后执行并开始通过。然后你会看到下一个断言失败。通过这种方式,您的测试确实得到测试,但仅在编写产品代码时进行。

4)如果我们从单元测试或功能测试中提取代码——创建一个用于许多测试的测试库,那么我们会对所有这些进行单元测试。

这对我们很有帮助。我们似乎一直 100% 遵守这些规则,我们对我们的安排非常满意。

于 2009-07-19T15:48:18.260 回答
2

回答

是的,应该测试您的 GUI 测试库。

例如,如果您的库提供了一个Check方法来根据二维数组验证网格的内容,您希望确保它按预期工作。

否则,用于测试网格必须接收特定数据的业务流程的更复杂的测试用例可能不可靠。如果您的Check方法中的错误导致误报,您将很快找到问题所在。但是,如果它产生误报,那么您将面临严重的麻烦。

要测试您的CheckGrid方法:

  • 使用已知值填充网格
  • 使用填充的值调用CheckGrid方法
  • 如果这种情况通过, CheckGrid至少有一个方面可以工作。
  • 对于第二种情况,您希望CheckGrid方法报告测试失败。
  • 您如何表示期望的细节将取决于您的 xUnit 框架(请参阅稍后的示例)。但基本上,如果CheckGrid没有报告测试失败,那么测试用例本身一定会失败。
  • 最后,您可能需要针对特殊情况多做几个测试用例,例如:空网格、网格大小与数组大小不匹配。

您应该能够为大多数框架修改以下 dunit 示例,以测试CheckGrid是否正确检测到错误:

begin
  //Populate TheGrid
  try
    CheckGrid(<incorrect values>, TheGrid);
    LFlagTestFailure := False;
  except
    on E: ETestFailure do
      LFlagTestFailure := True;
  end;
  Check(LFlagTestFailure, 'CheckGrid method did not detect errors in grid content');
end;

让我重申一下:应该测试您的 GUI 测试库;诀窍是——你如何有效地做到这一点?

TDD 流程建议您在实际实施之前首先弄清楚您打算如何测试新功能。原因是,如果你不这样做,你经常会发现自己摸不着头脑,不知道如何验证它是否有效。将测试用例改造到现有的实现上是极其困难的。

边注

你说的一件事让我有点困扰......你说需要“70%的时间来维护(你的测试)”

这对我来说听起来有点不对,因为理想情况下你的测试应该很简单,并且如果你的接口或规则发生变化,它们本身应该只需要改变。

我可能误解了你,但我的印象是你不写“生产”代码。否则你应该对测试代码和生产代码之间的切换周期有更多的控制,以减少你的问题。

一些建议:

  • 注意非确定性值。例如,日期和人工键可能会对某些测试造成严重破坏。你需要一个明确的策略来解决这个问题。(另一个答案本身。)
  • 您需要与“生产开发人员”密切合作,以确保您正在测试的接口的各个方面能够稳定下来。即,他们需要了解您的测试如何识别 GUI 组件并与之交互,这样他们就不会通过“不影响它们”的更改任意破坏您的测试。
  • 在前一点上,如果在进行更改时运行自动化测试会有所帮助。
  • 您还应该警惕太多简单地归结为任意排列的测试。例如,如果每个客户都有一个类别 A、B、C 或 D;然后 4 个“新客户”测试(每个类别 1 个)为您提供 3 个额外的测试,这些测试并不能真正告诉您比第一个测试更多,并且“难以”维护。
于 2010-06-19T21:32:40.530 回答
1

就个人而言,我不会对我的自动化库进行单元测试,而是针对基线的修改版本运行它们以确保所有检查点都能正常工作。这里的原则是我的自动化主要用于回归测试,例如当前运行的结果与预期结果相同(通常这等同于上次运行的结果)。通过针对一组经过适当修改的预期结果运行测试,所有测试都应该失败。如果他们没有,您的测试套件中有错误。这是从突变测试中借来的一个概念,我发现它非常适合检查 GUI 自动化套件。

于 2009-02-03T14:12:58.097 回答
1

从您的问题中,我可以理解您正在构建一个关键字驱动框架来执行自动化测试。在这种情况下,始终建议对常用和 GUI 实用功能进行一些白盒测试。由于您对在您的库中对每个 GUI 测试功能进行单元测试感兴趣,请继续努力。测试总是好的。这不是浪费时间,我将其视为您框架的“增值”。

您还提到了处理测试代码,如果您指的是测试方法,请将执行类似工作的不同功能/模块分组,例如:GUI 元素验证(存在)、GUI 元素输入、GUI 元素读取。对不同的元素类型进行分组,并为每个组执行类型单元测试方法。您可以更轻松地跟踪测试。干杯!

于 2013-10-25T14:51:08.327 回答
0

我建议测试测试是一个好主意,并且必须做一些事情。只需确保您为测试应用程序而构建的内容并不比应用程序本身更复杂。如前所述,即使在构建自动化功能测试时,TDD 也是一种好方法(我个人不会那样做,但无论如何它是一种好方法)。对测试代码进行单元测试也是一种好方法。恕我直言,如果您要自动化 GUI 测试,请继续进行任何可用的手动测试(您应该有步骤、原始场景、预期结果等),确保它们通过。然后,对于您可能创建且尚未手动编写脚本的其他测试,对它们进行单元测试并遵循 TDD 方法。(那么如果你有时间,你可以对其他的进行单元测试)。最后,关键字驱动,是,IMO,

于 2014-02-12T22:58:39.100 回答
0

You may want to explore a mutation testing framework ( if you work with Java : check out PIT Mutation Testing ). Another way to assess the quality of your unit testing is to look at reports provided by tools such as SonarQube ; the reports include various coverage metrics;

于 2014-02-28T22:41:55.663 回答