NUnit:在单个测试中运行多个断言

时间:2008-10-03 15:57:13

标签: unit-testing nunit

我被要求编写一个测试应用程序,需要在数据库中的多行上测试一个新的存储过程,本质上我想做这样的事情:


[Test]
public void TestSelect()
{
    foreach(id in ids)
    {
        DataTable old = Database.call("old_stored_proc",id);
        DataTable new_ = Database.call("new_stored_proc",id);

        Assert.AreEqual(old.Rows[0]["column"],ne_.Rows[0]["column"]);
    }
}

当我运行此测试时,如果1行与另一行不匹配,则整个测试失败;相反,我想计算断言通过多少次以及失败多少次。有没有办法用NUnit做到这一点?

我意识到NUnit可能有点过分,如果没有它,这是一个简单的任务......我只是想学习它。 ;)

8 个答案:

答案 0 :(得分:9)

好像你只是在说错话。如果要检查所有值,然后断言没有错误(或显示错误数),请尝试以下操作:

[Test]
public void TestSelect()
{
    int errors = 0;
    foreach(id in ids)
    {
        DataTable old = Database.call("old_stored_proc",id);
        DataTable new_ = Database.call("new_stored_proc",id);

        if (old.Rows[0]["column"] != new_.Rows[0]["column"])
        {
            errors++;
        }            
    }

    Assert.AreEqual(0, errors, "There were " + errors + " errors.");
}

答案 1 :(得分:5)

1)如果id是常量且未在测试运行时查找,则为每个id创建一个单独的单元测试夹具。这样你就会知道哪个id真的失败了。请参阅此处,了解数据驱动测试的问题:
http://googletesting.blogspot.com/2008/09/tott-data-driven-traps.html

2)如果你需要动态查找id,使得无法为每个id创建一个fixture,请使用akmad的建议进行一次更改。保留值不相等的id列表,并将列表添加到错误消息中。诊断一个仅指出错误数量的失败测试是非常困难的,因为你不知道哪个id会导致错误。

3)我不知道在NUnit中做的有多难,但在PyUnit中,当我们需要对动态生成的数据运行测试时,我们动态创建测试夹具并将它们附加到TestCase类,这样我们就可以了对每个未通过的数据进行测试失败。虽然我认为如果没有python的动态能力,这将会更加困难。

答案 2 :(得分:4)

我知道问题是关于NUnit的,但有趣的是,Gallio/MbUnit有一个允许一次运行并捕获多个断言的功能。

[Test]
public void MultipleTest()
{
    Assert.Multiple(() =>
    {
       Assert.IsTrue(blabla);
       Assert.AreEqual(pik, pok);
       // etc.
    }
}

Assert.Multiple正在捕获所有失败的断言,并将在测试结束时报告它们。

答案 3 :(得分:1)

我会计算不匹配的行数,然后编写一个断言,将这个数字与0进行比较,并返回消息中不匹配字符串的数量。

您也可以使用Assert.Greater

P.S。原则上你应该尝试每单元测试做一个断言。这就是它的要点。

答案 4 :(得分:0)

你可以声明一个计数器,然后断言计数器的值以确定通过/失败

此外,您可以在测试设置中完成大部分工作,然后只需创建多个测试。

我不清楚为什么你需要在同一个测试中使用所有断言stmts。

答案 5 :(得分:0)

根据您列出的目标,如果一行与另一行不匹配,整个测试应该失败。计算断言通过或失败的次数比您预期的结果与实际结果的比较所得的信息少。

答案 6 :(得分:0)

我最近遇到了同样的问题。我将计算错误的想法与Yann Trevin提到的Assert.Multiple结合成一个IEnumberable的扩展方法,让我做的事情如下:

[Test]
public void TestEvenNumbers()
{
    int[] numbers = new int[] { 2, 4, 12, 22, 13, 42 };
    numbers.AssertAll((num) => Assert.That((num % 2) == 0, "{0} is an odd number", num));
}

这导致NUnit输出:

TestEvenNumbers:
  5 of 6 tests passed; 0 inconclusive
FAILED: 13:   13 is an odd number
  Expected: True
  But was:  False

  Expected: 6
  But was:  5

OP问题的解决方案是:

[Test]
public void TestSelect()
{
    ids.AssertAll(CheckStoredProcedures);
}

private void CheckStoredProcedures(Id id)
{
    DataTable old = Database.call("old_stored_proc",id);
    DataTable new_ = Database.call("new_stored_proc",id);

    Assert.AreEqual(old.Rows[0]["column"], new_.Rows[0]["column"]);
}

这是扩展方法(注意我使用“All”而不是“Multiple”与Linq术语保持一致):

using System;
using System.Text;
using System.Collections.Generic;
using NUnit.Framework;

public static class NUnitExtensions
{
    public static void AssertAll<T>(this IEnumerable<T> objects, Action<T> test)
    {
        int total = 0;
        int passed = 0;
        int failed = 0;
        int inconclusive = 0;
        var sb = new StringBuilder();
        foreach (var obj in objects)
        {
            total++;
            try
            {
                test(obj);
                passed++;
            }
            catch (InconclusiveException assertion)
            {
                inconclusive++;
                string message = string.Format("INCONCLUSIVE: {0}: {1}", obj.ToString(), assertion.Message);
                Console.WriteLine(message);
                sb.AppendLine(message);
            }
            catch (AssertionException assertion)
            {
                failed++;
                string message = string.Format("FAILED: {0}: {1}", obj.ToString(), assertion.Message);
                Console.WriteLine(message);
                sb.AppendLine(message);
            }
        }

        if (passed != total)
        {
            string details = sb.ToString();
            string message = string.Format("{0} of {1} tests passed; {2} inconclusive\n{3}", passed, total, inconclusive, details);
            if (failed == 0)
            {
                Assert.Inconclusive(message);
            }
            else
            {
                Assert.AreEqual(total, passed, message);
            }
        }
    }
}

答案 7 :(得分:0)

如果ID是一个简单的硬编码列表,则可以使用[TestCase()] attribute

char*

这将为每个ID生成三个单独的测试,并且您使用的任何nunit测试运行程序都会显示通过/失败计数。

如果需要生成ID的动态列表,则建议使用[TestCaseSource()] attribute