代码之家  ›  专栏  ›  技术社区  ›  Eric J.

单元测试中逐步断言的值

  •  3
  • Eric J.  · 技术社区  · 14 年前

    在编写单元测试时,有些情况下,可以为可能失败的每个条件创建一个断言,或者创建一个捕获所有此类条件的断言。C举例:

    Dictionary<string, string> dict = LoadDictionary();
    // Optional Asserts:
    Assert.IsNotNull(dict, "LoadDictionary() returned null");
    Assert.IsTrue(dict.Count > 0, "Dictionary is empty");
    Assert.IsTrue(dict.ContainsKey("ExpectedKey"), "'ExpectedKey' not in dictionary");
    // Condition actually interested in testing:
    Assert.IsTrue(dict["ExpectedKey"] == "ExpectedValue", "'ExpectedKey' is present but value is not 'ExpectedValue'");
    

    在这种情况下,大型多人项目添加“可选断言”是否有价值?有更多的工作要做(如果你有很多单元测试的话),但是问题在哪里会更清楚。

    我正在使用Vs2010和集成测试工具,但我希望这个问题是通用的。

    4 回复  |  直到 14 年前
        1
  •  2
  •   ratkok    14 年前

    我认为这样做是有价值的,但你必须小心使用它。我还负责一个大型的多人项目,最近我们在单元测试策略中开始使用类似的方法。

    我们尝试对每个“执行路径”进行一次测试,并且我们有多个断言的测试用例。但是,我们在测试用例中使用致命和非致命断言,并且只有非致命断言用于具有多个断言的测试用例。致命断言也用于这些测试用例中(每个TC一个),以验证条件,如果失败,就没有必要断言其他任何东西。这种方法有助于我们更快地本地化错误,因为有时可能会发生多个断言。

    将它与定制日志结合起来,以提供有关故障的更多信息——测试和调试速度更快,实际上效率更高。

    但是,看看您的示例,我不确定“多个/可选断言”是否真的很好,因为很可能您不想反复测试这些基本功能(loadDict(),而不是空的等)。我认为在您的案例中,“测试用例设置”应该确保字典“不是空的”,并且loadDictionary()按预期执行(已经用特定的TCS进行了测试)。这个测试用例的目标似乎是验证查找方法,它应该只关注测试该方法。其他的一切都是设置/其他功能,实际上不应该属于这个TC。

        2
  •  1
  •   Paul Butcher    14 年前

    建议每个测试只有一个断言,这样,您就可以清楚地知道自己在测试什么。

    • 在TDD中,这将使得在测试失败后更容易准确地隔离您将要实现的内容。
    • 您的测试工具生成的报告将更加准确。

    这些“可选”断言中的每一个都让我觉得是独立的测试,但是不需要在每个测试中断言它们中的每一个。

    • 测试是否 LoadDictionary(); 返回空值,
    • 测试是否 loadDictionary(); 返回空字典
    • 等。

    没有包含所有这些断言的单个测试。当然,没有几个测试包含这些断言的大部分,后面跟着每个测试要检查的实际内容。

        3
  •  1
  •   Damovisa    14 年前

    就我个人而言,我认为预先回顾现有测试以插入可选断言没有太大的价值。

    如果这是一个现有的项目,希望您的所有测试都能通过。如果是这样的话,只有当您重构和/或更改或向代码中添加某些内容时,它们才会开始中断。

    我认为,如果测试失败,当测试失败时,处理这些测试会更容易。当然,您将得到一个测试的一般性失败,但是通过抛出一个断点(或者通常只是调查),您将很容易发现失败的真正来源。

    或者,当测试失败时,可以添加可选断言,然后澄清错误。这样,您就不会预先使用时间向不会失败的测试添加额外的断言,但这样做时仍然可以获得好处。


    但是,如果这是一个主动的行动(正如您所建议的那样),概述了测试的指导原则,我认为它实际上取决于测试本身以及您从附加断言中获得的好处。知道了会节省多少时间 dict 是否为空而不是只缺少一个键?最终,您应该只测试一件事情,所以如果您开始在一个测试中发现很多断言,那么可能是出了问题。

    就我个人而言 不要 认为一个支配所需断言的全球政策是值得实施的。我认为应该根据具体情况来决定。对于一些测试,比如您给出的示例,在一些附加断言中可能有很多价值。对于一些更简单但不太可能失败的事情,可能没有。

    强迫开发人员捕捉和描述每一个可能的离散故障是有点消极的。就像你期望它经常失败一样,节省几分钟诊断它是值得的。

        4
  •  1
  •   philant    14 年前

    重要的是,当条形图变红时,能够快速了解失效的原因。

    假设只有一个断言:

    Assert.IsTrue(dict["ExpectedKey"] == "ExpectedValue",
                  "'ExpectedKey' is present but value is not 'ExpectedValue'");
    

    当loadDictionary()返回空值时会发生什么?如果这将崩溃的整个单元测试应用程序将发生与C/C++,那么断言保护是必要的。如果故障消息清楚地表明dict为空,那么可选断言是无意义的。

    第二个问题是当期望的键没有出现在字典中时会发生什么?再一次,错误消息应该做出区分。如果测试与该键关联的值的断言引发了异常(例如缺少键异常),则可以:无需首先测试该键是否存在,然后测试其值。这里一个测试就足够了。

    字典为空的测试是无用的,因为此测试的目的似乎是验证某个键是否具有某个值。

    最后,使用某种相等断言将给出更准确的错误消息:

    Assert.Equal("ExpectedValue", dict["ExpectedKey"],
                  "Incorrect value for 'ExpectedKey'");
    

    错误应该类似于“expectedValue,actual:”。知道键的错误值可能会有所帮助。