如何要求至少N个断言在NUnit中运行

本文关键字:断言 NUnit 运行 | 更新日期: 2023-09-27 18:22:38

使用QUnit,我可以显式地expect指定数量的断言将在我的测试中运行(检查,默认情况下,无论如何至少需要一个断言,否则测试失败)。

有什么方法可以用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上编写自己的Extension方法,用一行代码就可以做到这一点,但我想知道是否有一种惯用/内置的方法可以用NUnit"期望N个断言"

如何要求至少N个断言在NUnit中运行

哪里没有内置的功能。建议自己实施。

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

[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)或类似的东西。

根据前面的答案进行讨论。Assert不是对您的场景有用的类。如果你真的想,就自己上课如果使用Assert。

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

我觉得它很难看。断言总是抛出错误,所以如果你想弄清楚,就用其他方法做吧。

因此,您可以重写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<MethodInfo,QContext>QContextAttributeStackTrace.GetFrame(...)来完成——如果显式QContext为null,则QAssert的查找StackTrace并找到具有Test属性的最接近的方法,然后它查看字典——如果仍然没有字典(在测试中第一次断言)——它使用QContextAttribute初始化它(如果附加到方法)——然后使用它,以下所有的QAssert调用都将使用缓存的字典。这是一个非常简单的实现——它没有释放缓存的dict,所以如果一个测试被调用两次,它将重用不太好的上下文,但在大多数情况下,这种模式可以工作。

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

我们曾经考虑在每次测试中都需要一个断言,但没有通过否则我们还考虑在这样一个案例两者都违反了一个相当常见的用法,即人们只需调用,验证是否未引发异常。我不喜欢这样的测试但由于有些人使用它们,我们觉得它们必须支持。

在同一线程的早些时候,还可以清楚地看到,NUnit用户无法访问运行的断言数量是故意的。给出的一些推理:

一般来说,我们认为您的测试不应该了解测试基础设施。NUnit的断言计数是私有的NUnit,直到它被统计为测试的最终结果。它可能包括在设置中、由测试本身甚至在拆卸-尽管不建议这样做。可能包括资产由放置在测试、fixture或高级套房。让您的测试知道将有多少断言执行时,它必须了解环境的几乎所有信息它在其中运行。这违背了测试的全部理念独立

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

你展示的"变通方法"几乎是正常的检查方式在NUnit、xUnit、mbUnit和mstest中调用了回调

也就是说,这种类型的测试:

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

一句话:在NUnit中,检查(或期望)运行的断言数量是不可能的。