【问题标题】:To what extent do you unit test functionality?您在多大程度上对功能进行单元测试?
【发布时间】:2010-11-11 19:24:34
【问题描述】:

我有一个关于单元测试的问题。我会用一个例子来概括它。

假设我正在编写一个命令行应用程序,它具有各种可接受的命令,并且每个命令都有其可接受的参数。例如:myapp create -DprojectName=newProject -DprojectVersion=1.0。

如果我有一个名为 Command 的类,它有一个 validateArguments 方法,该方法接收有效参数名称列表并将其与用户指定的参数列表进行比较,您将如何进行单元测试?

我看到的可能性是:

  1. 您编写了两个单元测试 - 一个确保传入有效参数时不会引发错误,另一个确保传入无效参数时引发错误。

  2. 您编写了一个单元测试,以确保所有可接受的参数都不会被拒绝。我最终会对每个可接受的参数进行单元测试:

    @测试 public void validateArgments_ProjectNameArgPassed_NoExcepton throw { ... }

    @测试 public void validateArguments_ProjectVersionArgPassed_NoException throw { ... }

等等。

对我来说,第一种方法是有意义的。但这并不能确保应该接受的每个论点都是。

【问题讨论】:

    标签: unit-testing testing automated-tests


    【解决方案1】:

    在不了解底层代码逻辑的情况下很难提出建议(单元测试是白盒测试而不是黑盒测试是有原因的),但我对该代码的处理方法是一套单元测试,其中包含以下测试:

    • 所有参数均无效
    • 所有参数都有效
    • 有效和无效参数的不同组合(用于测试上述未经测试的代码路径)
    • 不同类型的无效参数,例如未指定、格式不正确 (-projectVersion=Hippopotamus)、值不正确 (-projectVersion=99.0) 等。
    • 我可能想到的任何其他故障情况。

    我发现单元测试的真正好处不在于测试成功场景,因为一些简单的集成测试也可以提供这种好处。我发现真正的好处在于测试大量错误场景,因为通常很少运行的代码(即错误处理)包含可能会通过测试级别而被忽视的错误。

    【讨论】:

    • +1 你还为你修复的每个错误编写单元测试,这样你以后就不会导致回归。
    【解决方案2】:

    这在一定程度上取决于您使用的测试框架和语言。其中大多数(至少在 C# 中)允许您编写所谓的数据驱动测试。这允许您为测试方法提供要测试的参数,同时指定预期结果。

    例如,Gallio 的此类测试如下所示:

    [Row("prj1", "1.0", true)]
    [Row("blah", "Hippopotamus", false)]
    [Row(null, "1.0", false, ExpectedException = typeof(NullReferenceException))]
    public void TestArguments(string arg1, string arg2, bool expectedResult)
    {
        var result = myApp.ValidateArguments(args);
    
        Assert.AreEqual(expectedResult, result);
    }
    

    这样,您可以轻松测试所有需要测试的参数组合,并且不需要太多代码。

    在设计上不可能测试所有无效参数,这通常(大部分)对于有效参数也是如此 - 它们在理论上可能是有限的,但实际上有太多可能的组合。在这种情况下,您所能做的就是测试您能想到的可能、重要且有意义的组合。
    从最终用户的角度/业务方面对此有很大帮助 - 他们经常会提出远远超出开发人员幻想的用例......

    HTH.
    托马斯

    【讨论】:

      【解决方案3】:

      同意 dlanod,UNIT 测试的范围应仅限于测试系统中最小的可编译组件。您的测试方法更倾向于灰盒测试,并不是说这不是正确的方法。同样,很难确定应该使用哪种方法取决于代码/类的大小和复杂性。

      UNIT 测试的另一个主要目标是确定代码覆盖率,而不是功能覆盖率。实际上,不到 50% 的代码服务于 90% 的用例场景。如果您的应用程序很小,请使用灰盒方法,您可以在其中网格化 UNIT 和功能测试,否则最好有一个清晰的分离。希望这可以帮助。

      【讨论】:

        【解决方案4】:

        我倾向于按代码的重要性对其进行分解。

        如果是处理金钱的代码,或其他不能接受您的代码失败的功能,那么我会尝试测试所有可能的代码路径和结果。

        接下来是广泛使用的代码,易于重构或附加功能,可能是脆弱的代码,然后我测试我认为是常见的用例。如果我后来发现由于我错过了一个用例而导致某些东西被破坏,我会为那个用例添加一个测试。

        底部是影响较小的代码 - 格式化/显示代码;直接的值比较等,其中维护这些测试的成本超过了拥有 100% 正确代码的好处。

        【讨论】:

          猜你喜欢
          • 2016-12-13
          • 2020-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-25
          • 2021-10-12
          • 2022-11-29
          相关资源
          最近更新 更多