【问题标题】:Testing a test?测试测试?
【发布时间】:2009-02-03 13:36:43
【问题描述】:

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

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

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

【问题讨论】:

标签: unit-testing automated-tests


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

      但是,您的主要软件系统的实际单元测试不应增长到足以需要单元测试的地步。如果它们太复杂以至于它们需要单元测试,那么您需要认真重构您的软件并注意简化单元测试。

      【讨论】:

        【解决方案4】:

        您可能想看看Who tests the tests

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

        嗯?

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

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

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

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

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

        • 如果我要使用自制的测试仪,如果时间允许,我会测试它。
        • 如果它是我使用的第三方工具,我希望供应商已经对其进行了测试。

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

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

            最近http://blog.objectmentor.com/articles/2009/01/31/quality-doesnt-matter-that-much-jeff-and-joel的cmet里也有关于这个问题的讨论


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

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

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

            【讨论】:

              【解决方案7】:

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

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

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

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

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

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

              【讨论】:

                【解决方案8】:

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

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

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

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

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

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

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

                【讨论】:

                  【解决方案9】:

                  回答

                  是的,应该测试您的 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% 的时间来维护(你的测试)”

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

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

                  一些建议:

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

                  【讨论】:

                    【解决方案10】:

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

                    【讨论】:

                      【解决方案11】:

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

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

                      【讨论】:

                        【解决方案12】:

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

                        【讨论】:

                          【解决方案13】:

                          您可能想探索一个突变测试框架(如果您使用 Java:请查看 PIT Mutation Testing)。评估单元测试质量的另一种方法是查看由诸如 SonarQube 等工具提供的报告;报告包括各种coverage metrics

                          【讨论】:

                            猜你喜欢
                            • 2018-08-31
                            • 1970-01-01
                            • 2013-04-09
                            • 2011-12-02
                            • 2015-01-26
                            • 1970-01-01
                            • 2017-10-10
                            • 2018-03-23
                            • 1970-01-01
                            相关资源
                            最近更新 更多