单元测试中逐步断言的价值

时间:2010-06-01 00:04:15

标签: unit-testing

在编写单元测试时,有些情况下可以为可能失败的每个条件创建一个Assert,或者为可以捕获所有这些条件的Assert创建一个Assert。 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'");

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

我正在使用VS 2010和集成测试工具,但打算将问题视为通用。

4 个答案:

答案 0 :(得分:2)

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

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

将其与自定义日志相结合以提供有关故障的其他信息 - 测试和调试更快,实际上更高效。

但是,看看你的例子,我不确定“多个/可选断言”是否真的很好,因为你很可能不想一遍又一遍地测试这些基本功能(LoadDict(),而不是空等) 。我认为在您的情况下,“测试用例设置”应该确保Dictionary“不为空”并且LoadDictionary()按预期执行(已经使用特定TC进行了测试)。这个测试用例的目标似乎是验证查找方法,它应该专注于测试那个东西。其他一切都是设置/其他功能,不应该属于这个TC,真的。

答案 1 :(得分:1)

建议每次测试只有一个断言,这样,您就可以清楚地了解自己正在测试的内容。

  • 在TDD中,这将使您更容易准确地确定在测试失败后您将要实施的内容。
  • 您的测试工具生成的报告会更准确。

每个“可选”断言都将我视为单独的测试,但不需要在每次测试中断言每个测试。

  • 测试LoadDictionary();是否返回null,
  • 测试LoadDictionary();是否返回空字典

没有包含所有这些断言的单个测试。绝对没有几个测试包含大多数这些断言,然后是每个测试要检查的实际内容。

答案 2 :(得分:1)

就个人而言,我认为先发制人地重新审视插入可选断言的现有测试并不是很有价值。

如果这是一个现有项目,希望你的所有测试都通过。如果是这种情况,他们只会在您重构和/或更改或向代码添加内容时开始破解。

我认为处理破坏的测试会更容易。当然,你的测试会失败,但是通过投入一个断点(或者通常只是调查),你很容易发现你失败的真正根源。

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


但是,如果这是一个主动的行动(正如您所建议的那样),它概述了测试指南,我认为这实际上取决于测试本身以及您从其他断言中获得的好处。知道dict是空的而不是只是错过一个键,你真正节省了多少时间?最终,你应该只测试一件事,所以如果你在一次测试中开始发现很多断言,那么可能有些不对劲。

就个人而言,我认为规定所需断言的全局政策值得实施。我认为应该根据具体情况来决定。对于某些测试,例如您给出的示例,在某些其他断言中可能有很多值。对于不太可能失败的简单事物,可能没有。

迫使开发人员捕获并描述每个可能的离散故障都有点消极。就像你期望它经常失败一样,值得花几分钟来诊断它。

答案 3 :(得分:1)

重要的是能够在条形变红时快速了解故障的起因。

让我们假设只有一个断言:

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

当LoadDictionary()返回null时会发生什么?如果这会像C / C ++那样崩溃整个单元测试应用程序,那么断言保护是必要的。如果失败消息清楚地表明dict为null,那么可选的断言是没有意义的。

第二个问题是当预期的密钥没有出现在字典中时会发生什么?再次,错误消息应该区分。如果测试与密钥关联的值的断言会抛出异常(例如缺少密钥异常),那么就没问题了:首先不需要测试密钥是否存在,然后再测试其值。一个测试就够了。

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

最后,使用某种相等的断言可以提供更准确的错误消息:

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

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