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

问题描述 投票:0回答:8

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

[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 可能有点矫枉过正,如果没有它,这就是一个简单的任务......我只是想学习它。 ;)

c# unit-testing nunit
8个回答
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.");
}

5
投票

1) 如果 id 是恒定的并且在测试运行时未查找,则为每个 id 创建一个单独的单元测试装置。 这样您就会知道哪些 id 实际上出现了故障。 请参阅此处有关数据驱动测试问题的文章:
http://googletesting.blogspot.com/2008/09/tott-data-driven-traps.html

2) 如果您需要动态查找 id,从而无法为每个 id 创建固定装置,请使用 akmad 的建议进行一项更改。 保留值不相等的 id 列表,并将该列表添加到错误消息中。 诊断仅指出错误数量的失败测试将非常困难,因为您不知道是什么 id 导致了错误。

3)我不知道在 NUnit 中做起来有多困难,但是在 PyUnit 中,当我们需要对动态生成的数据运行测试时,我们动态创建测试装置并将它们附加到 TestCase 类,以便我们有一个对每条未通过的数据进行测试失败。 虽然我想如果没有 python 的动态能力,这会困难得多。


5
投票

我知道这个问题是专门关于 NUnit 的,但有趣的是,Gallio/MbUnit 有一个功能,允许同时运行和捕获多个断言。

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

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


1
投票

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

您也可以使用

Assert.Greater
来实现此目的。

附注原则上,您应该尝试为每个单元测试执行一个断言。这就是它的要点。


1
投票

如果是简单的硬编码 ID 列表,您可以使用

[TestCase()]
属性

[Test]
[TestCase(1234)]
[TestCase(5678)]
[TestCase(7654)]
public void TestSelect(int 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"]);
}

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

如果需要生成动态 ID 列表,建议使用

[TestCaseSource()]
属性


0
投票

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

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

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


0
投票

根据您制定的目标,如果一行与另一行不匹配,则整个测试应该失败。 与将预期结果与实际得到的结果进行比较相比,计算断言通过或失败的次数所提供的信息要少。


0
投票

我最近也遇到了同样的问题。我将计算错误的想法与 Yann Trevin 提到的

Assert.Multiple
结合到了
IEnumerable<T>
的扩展方法中,它让我可以执行以下操作:

[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"]);
}

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

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);
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.