我被要求编写一个测试应用程序,需要在数据库中的多行上测试新的存储过程,本质上我想做这样的事情:
[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 可能有点矫枉过正,如果没有它,这就是一个简单的任务......我只是想学习它。 ;)
看起来你只是断言了错误的事情。 如果您想检查所有值,然后断言没有错误(或显示错误数量),请尝试以下操作:
[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) 如果 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 的动态能力,这会困难得多。
我知道这个问题是专门关于 NUnit 的,但有趣的是,Gallio/MbUnit 有一个功能,允许同时运行和捕获多个断言。
[Test]
public void MultipleTest()
{
Assert.Multiple(() =>
{
Assert.IsTrue(blabla);
Assert.AreEqual(pik, pok);
// etc.
}
}
Assert.Multiple
正在捕捉所有失败的断言,并将在测试结束时报告它们。
我会计算不匹配的行数,然后编写一个断言,将该数字与 0 进行比较,并返回消息中不匹配字符串的数量。
您也可以使用
Assert.Greater
来实现此目的。
附注原则上,您应该尝试为每个单元测试执行一个断言。这就是它的要点。
[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 测试运行程序都将显示通过/失败计数。
[TestCaseSource()]
属性。
你可以声明一个计数器,然后断言计数器的值来确定通过/失败
此外,您可以在测试设置中完成大部分工作,然后创建多个测试。
我不清楚为什么你需要在同一个测试中使用所有断言stmts。
根据您制定的目标,如果一行与另一行不匹配,则整个测试应该失败。 与将预期结果与实际得到的结果进行比较相比,计算断言通过或失败的次数所提供的信息要少。
我最近也遇到了同样的问题。我将计算错误的想法与 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);
}
}
}
}