【问题标题】:NUnit: Running multiple assertions in a single testNUnit:在单个测试中运行多个断言
【发布时间】:2008-10-03 15:57:13
【问题描述】:

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

[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


    【解决方案1】:

    似乎您只是在断言错误的事情。如果您想检查所有值,然后断言没有错误(或显示错误数量),请尝试以下操作:

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

    【讨论】:

      【解决方案2】:

      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 的动态能力,这将更加困难。

      【讨论】:

      • 谢谢,我真正想要的是你的#3...但就像你说的...这是.net :(
      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

        您也可以为此使用Assert.Greater

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

        【讨论】:

          【解决方案5】:

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

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

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

          【讨论】:

          • 我想断言存储过程对数据库中的每一行都有效。
          【解决方案6】:

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

          【讨论】:

            【解决方案7】:

            我最近遇到了同样的问题。我将计数错误的想法与 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"]);
            }
            

            这是扩展方法(请注意,为了与 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);
                        }
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

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

              [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()] attribute

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2013-04-16
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-03-16
                • 2011-02-22
                • 1970-01-01
                相关资源
                最近更新 更多