Unit testing 单元测试写作的最佳实践

Unit testing 单元测试写作的最佳实践,unit-testing,automated-tests,Unit Testing,Automated Tests,我有一个“最佳实践”问题。我正在为某个方法编写一个测试,但是有多个条目值。我应该为每个条目值编写一个测试,还是应该更改entryValues变量值,并调用.assert()方法(对所有可能的值范围执行此操作) 谢谢你的帮助。 致以最良好的祝愿 佩德罗·马盖亚 编辑:我正在使用.NET。Visual Studio 2010与VB。最好对每个输入/输出集进行单独的单元测试,涵盖您尝试测试的方法的所有可能值(或至少是要进行单元测试的输入/输出集).如果一个人必须编写许多只在初始输入和最终输出中变化的测

我有一个“最佳实践”问题。我正在为某个方法编写一个测试,但是有多个条目值。我应该为每个条目值编写一个测试,还是应该更改entryValues变量值,并调用.assert()方法(对所有可能的值范围执行此操作)

谢谢你的帮助。 致以最良好的祝愿

佩德罗·马盖亚


编辑:我正在使用.NET。Visual Studio 2010与VB。

最好对每个输入/输出集进行单独的单元测试,涵盖您尝试测试的方法的所有可能值(或至少是要进行单元测试的输入/输出集).

如果一个人必须编写许多只在初始输入和最终输出中变化的测试,那么他应该使用数据驱动测试。这允许您定义一次测试以及输入和输出之间的映射。然后,单元测试框架将其解释为每个案例一个测试。如何真正做到这一点取决于您使用的是哪种框架。

您是在谈论这种差异吗

- (void) testSomething
{
    [foo callBarWithValue:x];
    assert…
}

- (void) testSomething2
{
    [foo callBarWithValue:y];
    assert…
}
vs


第一个版本更好,因为当测试失败时,您将更好地了解什么不起作用。第二个版本显然更方便。有时我甚至将测试值塞进一个集合以节省工作。当我可能只想单独调试单个案例时,我通常会选择第一种方法。当然,我只在测试值真正属于一起并形成一个连贯的单元时才选择后者。

您有两个选择,实际上,您没有提到您使用的测试框架或语言,因此其中一个可能不适用

1) 如果您的测试框架支持它,请使用行测试,MBUnit和Nunit支持此功能如果您使用.NET,这将允许您在方法上放置多个属性,并且每一行都将作为单独的测试执行

2) 如果没有按照条件编写测试,请确保给它一个有意义的名称,这样,如果(当)测试失败,您可以很容易地找到问题,并且它对您有意义

编辑 它在努尼特被称为TestCase

  • 较小的测试更容易阅读
  • 该名称是测试文档的一部分
  • 单独的方法可以更精确地指出失败的原因
  • 因此,如果您有一种方法,如:

    void testAll() {
      // setup1
      assert()
      // setup2
      assert()
      // setup3
      assert()
    }
    
    根据我的经验,这会很快变得很大,因此很难阅读和理解,因此我会:

    void testDivideByZero() {
      // setup
      assert()
    }
    
    void testUnderflow() {
      // setup
      assert()
    }
    
    
    void testOverflow() {
      // setup
      assert()
    }
    
    我应该为每个条目写一个测试吗 值,还是应该更改 entryValues变量值,并调用 .assert()方法(为所有人执行此操作 可能值的范围)

    如果只有一个代码路径,通常不会测试所有可能的输入。您通常想要测试的是“有趣的”输入,它们是您将获得的数据的良好示例

    例如,如果我有一个函数

    define add_one(num) {
      return num+1;
    }
    
    我不能为所有可能的值编写测试,所以我可以使用MAX_NEGATIVE_INT,-1,0,1,MAX_POSITIVE_INT作为我的测试集,因为它们很好地代表了我可能得到的有趣值

    每个代码路径至少应有一个输入。如果您有一个函数,其中每个值对应于唯一的代码路径,那么我会考虑为可能的值的完整范围编写一个测试。例如命令解析器

    define execute(directive) {
      if (directive == 'quit') { exit; }
      elsif (directive == 'help') { print help; }
      elsif (directive == 'connect') { intialize_connection(); }
      else { warn("unknown directive"); }
    }
    

    为了清晰起见,我使用了elifs而不是调度表。我认为这清楚地表明,输入的每个唯一值都有不同的行为,因此您需要测试每个可能的值。

    Hi Kev,要搜索RowTest。这似乎是一个很好的方法。谢谢大家,还有所有花时间回答问题的人。数据驱动(参数化)测试在理论上听起来不错,但我认为它们使测试用例更难理解和维护。对每个用例有一个单独的测试和一个描述性的名称有助于我更好地理解用例。公平地说,这意味着一些重复的代码,但是重构可以最小化到一个可承受的水平。@Peter-参数化测试还包含一个描述和一个名称属性以确保可读性-至少在NUnit中是这样。我使用数据驱动测试,当单元测试中的所有步骤都是恒定的,只有输入输出发生变化时。
    define execute(directive) {
      if (directive == 'quit') { exit; }
      elsif (directive == 'help') { print help; }
      elsif (directive == 'connect') { intialize_connection(); }
      else { warn("unknown directive"); }
    }