是Assert.Fail()被认为是不良做法吗?

时间:2020-03-06 14:35:43  来源:igfitidea点击:

在执行TDD时,我经常使用Assert.Fail。我通常一次只进行一个测试,但是当我对以后要实现的事情有想法时,我会迅速编写一个空测试,其中测试方法的名称表示要实现的待办事项列表。为了确保我不会忘记,我将Assert.Fail()放在主体中。

尝试xUnit.Net时,我发现他们尚未实现Assert.Fail。当然,我们始终可以Assert.IsTrue(false),但这也无法传达我的意图。我给人的印象是Assert.Fail不是故意实现的。这被认为是不良做法吗?如果可以,为什么?

@马丁·梅雷迪思(Martin Meredith)
那不是我所做的。我确实先编写了一个测试,然后实现代码以使其正常工作。通常我一次想到几个测试。或者,我考虑在做其他事情时要编写的测试。那是我编写一个空的失败测试要记住的时候。等我开始编写测试时,我会先做好测试工作。

@吉姆
看起来是个好主意。被忽略的测试不会失败,但是它们仍会显示在单独的列表中。必须尝试一下。

@马特·豪威尔斯
好想法。在这种情况下,NotImplementedException传达的意图比assert.Fail()更好。

@米奇·麦特
那就是我想要的。似乎它被排除在外,以防止以我滥用它的另一种方式被滥用。

解决方案

我将MbUnit用于单元测试。他们有一个选项可以忽略测试,该测试在测试套件中显示为橙色(而不是绿色或者红色)。也许xUnit具有类似的含义,并且意味着我们甚至不必在该方法中添加任何断言,因为它会以令人讨厌的不同颜色显示,从而使其难以错过?

编辑:

在MbUnit中,它是通过以下方式进行的:

[Test]
[Ignore]
public void YourTest()
{ }

如果我们编写的测试只是失败了,然后为其编写代码,然后编写该测试。这不是测试驱动的开发。

从技术上讲,如果我们正确使用测试驱动的开发,则不需要Assert.fail()。

我们是否考虑过使用待办事项列表或者将GTD方法应用于工作?

它被故意遗漏了。这是布拉德·威尔逊(Brad Wilson)关于为什么没有Assert.Fail()的答复:

We didn't overlook this, actually. I
  find Assert.Fail is a crutch which
  implies that there is probably an
  assertion missing. Sometimes it's just
  the way the test is structured, and
  sometimes it's because Assert could
  use another assertion.

对于这种情况,我没有执行以下操作(而不是调用Assert.Fail)(在C / NUnit中)

[Test]
public void MyClassDoesSomething()
{
    throw new NotImplementedException();
}

它比Assert.Fail更明确。

似乎已经普遍同意,使用比Assert.Fail()更明确的断言是可取的。尽管大多数框架都必须包括它,因为它们没有提供更好的选择。例如,NUnit(和其他)提供了ExpectedExceptionAttribute来测试某些代码抛出特定的异常类。但是,为了测试将异常的属性设置为特定值,不能使用它。相反,我们必须求助于Assert.Fail:

[Test]
public void ThrowsExceptionCorrectly()
{
    const string BAD_INPUT = "bad input";
    try
    {
        new MyClass().DoSomething(BAD_INPUT);
        Assert.Fail("No exception was thrown");
    }
    catch (MyCustomException ex)
    {
         Assert.AreEqual(BAD_INPUT, ex.InputString); 
    }
}

xUnit.Net方法Assert.Throws使此过程更加整洁,而无需使用Assert.Fail方法。通过不包括Assert.Fail()方法,xUnit.Net鼓励开发人员查找和使用更明确的替代方案,并在必要时支持创建新的断言。

MS Test具有Assert.Fail(),但也具有Assert.Inconclusive()。我认为,对于Assert.Fail()来说,最合适的用法是,如果我们有一些内联逻辑,这些逻辑在声明时会很尴尬,尽管我什至都没有想到任何好的示例。在大多数情况下,如果测试框架支持Assert.Fail()以外的其他功能,则可以使用它。

就个人而言,只要我们最终在实现代码通过之前就开始编写测试,我就可以将测试套件用作这样的待办事项列表没有问题。

话虽如此,我过去常常自己使用这种方法,尽管现在我发现这样做使我走下了预先编写过多测试的道路,这很奇怪,就像完全不编写测试的反向问题:我们最终过早做出恕我直言的设计决策。

顺便说一句,在MSTest中,标准测试模板在其样本末尾使用Assert.Inconclusive。

AFAIK xUnit.NET框架旨在非常轻巧,是的,它们确实故意删除了Fail,以鼓励开发人员使用显式的失败条件。

我一直使用Assert.Fail()处理一些案例,在这些案例中,我们发现测试应该通过简单的值比较之外的逻辑失败。举个例子:

try 
{
  // Some code that should throw ExceptionX
  Assert.Fail("ExceptionX should be thrown")
} 
catch ( ExceptionX ex ) 
{
  // test passed
}

因此,在我的框架中缺少Assert.Fail()似乎是一个错误。我建议修补Assert类以使其包含Fail()方法,然后将该修补程序以及添加该修补程序的理由提交给框架开发人员。

至于创建故意在工作区中失败的测试的做法,提醒自己在提交之前实施它们,这对我来说似乎是一种好习惯。

大胆的猜测:扣留Assert.Fail旨在阻止我们认为编写测试代码的好方法是将大量意大利面条加到导致坏情况下的Assert.Fail。 [编辑添加:别人的回答大致上证实了这一点,但带有引号]

由于这不是我们要执行的操作,因此xUnit.Net可能保护过度。

或者,也许他们只是认为它是如此罕见,如此不正交,以至于不必要。

我更喜欢实现一个名为ThisCodeHasNotBeenWrittenYet的函数(实际上更短一些,以便于键入)。无法比这更清楚地传达意图,并且我们有一个精确的搜索词。

可以更改它是否失败,是否未实现(以引起链接器错误),或者是未编译的宏,以适合我们当前的偏好。例如,当我们要运行已完成的操作时,我们会失败。当我们坐下来摆脱所有这些时,我们可能需要编译错误。

有了好的代码,我通常会这样做:

void goodCode() {
     // TODO void goodCode()
     throw new NotSupportedOperationException("void goodCode()");
}

我通常使用测试代码来执行以下操作:

@Test
void testSomething() {
     // TODO void test Something
     Assert.assert("Some descriptive text about what to test")
}

如果使用JUnit,并且不想出错,但是报错,那么我通常会这样做:

@Test
void testSomething() {
     // TODO void test Something
     throw new NotSupportedOperationException("Some descriptive text about what to test")
}

为什么要使用Assert.Fail表示应该引发异常?那是没有必要的。为什么不只使用ExpectedException属性?

这是为要通过设计引发异常的代码编写测试时使用的模式:

[TestMethod]
public void TestForException()
{
    Exception _Exception = null;

    try
    {
        //Code that I expect to throw the exception.
        MyClass _MyClass = null;
        _MyClass.SomeMethod();
        //Code that I expect to throw the exception.
    }
    catch(Exception _ThrownException)
    {   
        _Exception = _ThrownException
    }
    finally
    {
        Assert.IsNotNull(_Exception);
        //Replace NullReferenceException with expected exception.
        Assert.IsInstanceOfType(_Exception, typeof(NullReferenceException));
    }
}

恕我直言,这是比使用Assert.Fail()更好的测试异常的方法。这样做的原因是,我不仅要测试是否抛出了所有异常,而且还要测试异常类型。我意识到这类似于Matt Howells的回答,但是IMHO使用finally块更健壮。

显然,仍然可以包括其他Assert方法来测试异常输入字符串等。对于我们对我的模式的评论和看法,我将不胜感激。