【问题标题】:How to require at least N assertions to be run in NUnit?如何要求在 NUnit 中运行至少 N 个断言?
【发布时间】:2015-11-18 14:31:16
【问题描述】:

使用 QUnit,我可以 explicitly expect 在我的测试中运行特定数量的断言(哎呀,默认情况下无论如何都需要至少 1 个断言,否则测试失败)。

有没有办法用 NUnit 做类似的事情? (哎呀,默认情况下没有断言的测试甚至可以通过......)。

为了澄清,这里有一个例子/repro。假设我正在测试这段代码:

public class Util
{
    public static void MyMethod(Action someAction)
    {
        // We're TDD'ing, and we are *just about* to write:
        //someAction();
    }
}

然后在我写someAction(); 电话之前,我想写一个测试。这是我想要编写的测试:

[Test]
public void MyMethod_WhenGivenAction_ShouldCallAction()
{
    Assert.Expect(1); // Pseudo code, but this doesn't work.
    Util.MyMethod(() => Assert.That(true));
}

当然我可以解决这个问题:

[Test]
public void MyMethod_WhenGivenAction_ShouldCallAction()
{
    bool wasCalled = false;
    Util.MyMethod(() => wasCalled = true);
    Assert.That(wasCalled);
}

我想我什至可以在Assert 上编写自己的扩展方法来在一行中执行此操作,但我想知道是否有一种惯用的/内置方式来“预期N 个断言”与NUnit?

【问题讨论】:

标签: c# unit-testing nunit


【解决方案1】:

哪里没有内置工具。 建议自己实现。

如果你只想要一次,它可能是这样的:

[TestFixture]
public class myfixture {
    [Test]
    public void MyAssertCheckTest(){ // this test will fail if all other test fails
        var mytests = new Action[]{Test1,Test2};
        int failcount = mytests.Length;
        foreach(var test in mytests){
            try{
                test();
            }catch{
                if(--failcount==-1){
                    Assert.Fail("too many fails");
                }
            }
        }
    }

    [Test]
    [Explicit] //mark it explicit - we want to check it only if MyAssertCheckTest fails
    public void Test1(){
        Assert.True(false);
    }

    [Test]
    [Explicit]
    public void Test2(){
        Assert.True(true);
    }   
}

这个示例展示了在 NUnit 中没有这些东西的原因之一 - 它的语义不是很清楚(为什么我们允许某些事情失败?)

如果您想像往常一样使用它,我认为最好的方法是使用 [Suite] 而不是 [TestFixture] 并实现自己的下划线测试调用者。

或者制作自己的MyAsserts.AtLeast(int failcount, params Action[] subtests) 或类似的东西。

【讨论】:

  • 我很感激你的努力,但即使是一种解决方法,这对我来说也太老套了。这会产生各种奇怪的副作用(例如,这会破坏测试资源管理器和测试结果的可读性,并且会否定 NUnit 围绕隔离测试的特性),这远远超过了最初的症状。
  • 顺便说一句,重新阅读答案,我不清楚如何使用此代码来预期特定测试中的 N 个断言?
  • 我认为 NUnit Assert 的设计和意思并不完全符合您的期望。而且我认为您会同意 QUnit 的意识形态非常奇特(我并不是说它很糟糕,而是令人兴奋)-因为 Assert 的语义意味着某事应该是或否则它是错误的。测试失败意味着有什么不好。我不了解 TDD 中的 QUnit 功能 - 为什么会出现问题而不打扰我?所以试着对 NUnit 撒谎,并试图证明它失败的测试/断言没有失败 - 会是 hackish,虽然它真的是 hack ))))))
  • 我不确定我是否遵循您的最后评论(某些语言问题可能是其中的一部分),但无论如何我确实认为“QUnit 的意识形态相当异国情调”。相反,我认为 QUnit 非常适合 xUnit 系列测试框架,并且在许多方面与 NUnit 非常相似。
  • 另外,再次重申我的第二条评论:我很欣赏我的努力,但我看不到如何使用此答案中的代码来断言已经发生了一定数量的断言。该代码与我在问题中给出的上下文/代码方式不同...
【解决方案2】:

通过上一个答案下的讨论。 Assert 不是对您的场景有用的类。如果你真的想自己上课 如果使用断言。

var failcount = 3;
try{
   Assert.AreEqual(false,true);
}catch(AssertException e){
   if((--failcount)==-1){
     throw e;
   }
}

我觉得它很丑。 Assert 总是抛出错误,所以如果你想明确说明,请以其他方式进行。

所以你可以重写 NUnit 并添加这样做的可能性并提出池请求,如果没有,它仍然需要编写

作为变体: 1)为ex QAssert编写自己的Assert,可以选择获取定义FailCount的QContext,它可以是Assert的包装器

public static QAssert {
public static bool AreEqual ( object a, object b, QContext context ) { //while it's not fail sometimes while it's not AreEqual it should return bool
    if(null==context || context.FailAvailable<=0) {
        Assert.AreEqual(a,b);
        return true;
    }
    try{
        Assert.AreEqual(a,b);
        return true;
    }catch(AssertExceptio e){
        context.FailAvailable--;
        context.FailedTests.Add(MethodBase.GetCurrentMethod());
        return false;
    }
}
}

然后在你的测试中

[Test]
public void MultiFail(){
var context = new QContext{AvailFail=2};
QAssert.False(true, context); //AvailFail=1
QAssert.True(false, context); //AvailFail=2
QAssert.True(true, context);
}

测试将通过

如果你真的想让它更酷,你可以用 Attributes、StackTrace 创造你自己的魔法,并教你的 QAssert 自动初始化 QContext 并使用它:

[Test]
[QContext(AvailFail=2)]
public void MultiFail(){
QAssert.False(true); //AvailFail=1
QAssert.True(false); //AvailFail=2
QAssert.True(true);
}

它确实可以通过内部静态Dictionary&lt;MethodInfo,QContext&gt;QContextAttributeStackTrace.GetFrame(...) 来完成 - 如果显式 QContext 为空(测试中的第一个断言) - 它使用 QContextAttribute 初始化它(如果附加到方法) - 然后它使用它,所有后续 QAssert 调用将使用缓存字典。 这是一个非常简单的实现——它不是免费的缓存字典,所以如果一个测试会被调用两次,它将重用不太好的上下文,但在大多数情况下,这个模式可以工作。

【讨论】:

    【解决方案3】:

    首先,对于某些上下文(因为这个问题似乎主要是关于“N=1”(即每个测试中至少有一个断言))NUnit 的作者之一给出了一些关于on Google Groups 的见解:

    我们曾经考虑在每个测试中都需要一个断言,但失败了 除此以外。我们还考虑在这种情况下给出一个不确定的结果 案子。两者都违反了人们简单地制作的相当普遍的用法 一个调用,验证没有抛出异常。我不喜欢这样的测试 我自己,但由于有些人使用它们,我们觉得它们必须是 支持的。

    在同一线程的早些时候,很明显,NUnit 用户无权访问已运行的断言数量是故意的。给出的一些推理:

    一般来说,我们认为您的测试不应该知道任何关于 测试基础设施。 NUnit 的断言计数是私有的 NUnit,直到它被统计为测试的最终结果。它可能 包括在设置中执行的断言,由测试本身甚至在 拆解 - 尽管不建议这样做。它可能包括断言 由放置在测试、夹具或 更高级别的套房。让您的测试知道有多少断言 执行后,它必须了解有关环境的几乎所有信息 它正在其中运行。这破坏了测试的整个想法 独立。

    简而言之,这是不可能的。要么使用@ФагимСадыков 给出的其他答案之一,要么遵循 Google Group 的以下建议:

    您展示的“解决方法”几乎是正常的检查方式 在 NUnit、xUnit、mbUnit 和 mstest 中调用了回调

    也就是这种类型的测试:

    [Test]
    public void MyMethod_WhenGivenAction_ShouldCallAction()
    {
        bool wasCalled = false;
        Util.MyMethod(() => wasCalled = true);
        Assert.That(wasCalled);
    }
    

    底线:在 NUnit 中检查(或预期)运行的断言数量是不可能的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 2019-05-15
      • 1970-01-01
      • 2021-08-06
      • 2014-09-12
      • 2022-01-13
      • 1970-01-01
      相关资源
      最近更新 更多