Unit testing 在单个测试中检查几个不同的示例输入?

Unit testing 在单个测试中检查几个不同的示例输入?,unit-testing,tdd,nunit,Unit Testing,Tdd,Nunit,假设我想写一个验证电子邮件地址的函数 用正则表达式。我编写了一个小测试来检查我的函数,并编写了 实际功能。让它通过 然而,我可以想出许多不同的方法来测试相同的问题 作用(test@test.com; test234@test.com;test.test.com等) 我是否把所有需要检查的咒语都放在同一张单张上 使用多个断言进行测试,还是为每个断言编写一个新的测试 我能想到什么 谢谢 大多数测试框架现在都支持某种基于数据的测试,以便在多个数据集上运行相同的测试 看看努尼特的故事 xUnit.net

假设我想写一个验证电子邮件地址的函数 用正则表达式。我编写了一个小测试来检查我的函数,并编写了 实际功能。让它通过

然而,我可以想出许多不同的方法来测试相同的问题 作用(test@test.com; test234@test.com;test.test.com等)

我是否把所有需要检查的咒语都放在同一张单张上 使用多个断言进行测试,还是为每个断言编写一个新的测试 我能想到什么


谢谢

大多数测试框架现在都支持某种基于数据的测试,以便在多个数据集上运行相同的测试

看看努尼特的故事


xUnit.net、MBUnit和其他人有类似的方法。

我认为您不应该为每个案例编写单独的测试,因为所有案例都与测试字符串是否为正确电子邮件地址的相同内容相关。如果使用MbUnit或NUnit运行测试,则可以使用RowTest和Row属性向测试传递不同的值。另一种方法是将所有不同的电子邮件格式存储在一个数组中,并在该数组上循环并执行断言

如果不同的咒语真的归结为同一件事,那不是问题

但是,如果其中一个电子邮件地址突然中断了测试,您将不得不进行调试,以找出哪种情况出错。因此,这似乎是一个很好的理由来打破他们


您最终将进行数百万个单元测试,这是正确的,因为它们毕竟是在测试应用程序的一个单元,但在实践中,如果失败的断言破坏测试的方式不会弄乱失败的含义,那么多个断言就可以了。

如果您正在测试相同的功能,如果测试足够简单,我会在同一个测试中使用多个断言检查它们

有些人主张每个测试必须只有一个断言,因为测试必须非常、非常、非常简单

如果测试不简单,比如说您有循环和ifs,那么您需要对测试本身进行测试,以检查其逻辑是否正确,这是不好的


如果您的测试有多个断言,但仍然保持简单(没有循环,没有ifs)多个断言正在测试同一件事情,那么我就不会那么积极地提倡“每个测试一个断言”。您可以选择。

通常我创建许多不同的测试,并为每个测试指定自己的名称。例如,假设有3个不同的正则表达式{A、B和C}用于匹配电子邮件地址。该函数检查传入的电子邮件是否匹配,并接受找到的第一个匹配项

我将进行以下测试

  • A类型电子邮件应匹配模式A
  • B类型电子邮件应匹配模式B
  • cTypeEmailshouldMatchPattern C
  • BadEmail不应匹配任何模式
  • EmailCompatibleWithPattern A和Pattern B应该由A匹配
  • 电子邮件兼容模式A和C应该由A匹配
  • EmailCompatibleWithPatternBandC应与B匹配
通常,我会在每个测试中放置一个断言

然而,有时,如果断言都在检查同一事物的不同部分,我会在测试中放置多个断言。例如,假设我有一个函数,可以找到所有与模式a匹配的电子邮件。我的测试在电子邮件列表中输入,但只有一个与模式a匹配。该函数返回一个列表,我希望该列表中只有一个元素。因此,我主张两件事:

  • 列表中有一个元素
  • 其中一个元素是与模式A匹配的电子邮件

  • 正如@Paul提到的,有几个测试框架支持行测试。使用该功能,您可以编写以下内容:

    [TestCase ("test@test.com", true)]
    [TestCase ("x!x@test.com", true)]
    [TestCase ("x#x@test.com", true)]
    [TestCase ("x$x@test.com", true)]
    [TestCase ("x%x@test.com", true)]
    [TestCase ("x&x@test.com", true)]
    [TestCase ("x'x@test.com", true)]
    [TestCase ("x*x@test.com", true)]
    [TestCase ("x+x@test.com", true)]
    [TestCase ("x-x@test.com", true)]
    [TestCase ("x/x@test.com", true)]
    [TestCase ("x=x@test.com", true)]
    [TestCase ("x?x@test.com", true)]
    [TestCase ("x^x@test.com", true)]
    [TestCase ("x_x@test.com", true)]
    [TestCase ("x`x@test.com", true)]
    [TestCase ("x{x@test.com", true)]
    [TestCase ("x{x@test.com", true)]
    [TestCase ("x|x@test.com", true)]
    [TestCase ("x}x@test.com", true)]
    [TestCase ("x~x@test.com", true)]
    [TestCase ("test", false)]
    [TestCase ("", false)]
    [TestCase (null, false)]
    public void IsEmail_Should_Match_Valid_Email_Addresses(string target, bool result)
    {
        Assert.AreEqual(result, target.IsEmail());
    }
    

    或者你也可以用一堆资产做同样的事情。执行某些操作后,通常会在对象上断言多个属性。不过,我认为上述解决方案更具可读性。

    BDD和/或上下文/规范框架通过将每一批相关断言视为一个单独的观察块来管理这一点,每个观察块都有一个名称或描述性标签

    良好测试的关键要素是:

  • 每一个阶段都有AAA/GWT分离——你不得不真正思考哪个阶段是哪个阶段[并且通常会很快意识到什么时候事情需要进一步分解]
  • 在观察结果中,您最终得到了良好的名称/描述(行测试最多只能给您留下[不太可维护的]注释)
  • 当24个测试中的第10个和第17个测试[与行测试一样]时,您拥有每个测试的状态,以帮助您缩小解决方案的范围
  • 在某些情况下,当您真正在做一套简单的基于表或矩阵的事情时,行测试是合适的


    另外,xUnit.net的PropertyData在某些情况下可以作为在其他anwers中执行某些技巧的一种强大而适当的方法。

    Duplicate:在测试中可以没有测试和循环,但仍然可以有一些断言。当然,我在第一句中写到了这一点。无论如何,在你发表评论之后,我试图让“if and loop”这句话更清楚。在mbUnit中的RowTests,我错过了在visualstudio集成测试中的那些测试!