断言失败()被认为是不好的做法吗?

我使用 Assert。在进行 TDD 时经常会失败。我通常一次只处理一个测试,但是当我得到想要实现的东西时,我会迅速编写一个空的测试,其中测试方法的名称表明我想要实现的东西作为一种待办事项列表。为了确保我不会忘记。身体不适。

当试用 xUnit 时。我发现他们没有实现 Assert。失败。当然你可以一直断言 Assert.IsTrue (false) ,但是这并不能表达我的意图。我觉得 Assert。失败不是故意实现的。这算是坏习惯吗?如果是这样,为什么?


@ Martin Meredith 我可不是这么做的。我首先编写一个测试,然后实现代码使其工作。通常我会同时考虑几个测试。或者当我在做其他事情的时候,我会考虑写一个测试。这时我写了一个空的失败测试来记住。当我开始写测试的时候,我整洁地先写测试。

@ Jimmeh 看起来是个好主意。被忽略的测试不会失败,但是它们仍然显示在一个单独的列表中。我得试试。

@ Matt Howells 伟大的想法。异常比断言更好地传达意图。在这种情况下,Fail ()

@ 米奇・维特 这就是我要找的。似乎是为了防止它被另一种方式滥用,我滥用它。

103673 次浏览

I use MbUnit for my Unit Testing. They have an option to Ignore tests, which show up as Orange (rather than Green or Red) in the test suite. Perhaps xUnit has something similar, and would mean you don't even have to put any assert into the method, because it would show up in an annoyingly different colour making it hard to miss?

Edit:

In MbUnit it is in the following way:

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

If you're writing a test that just fails, and then writing the code for it, then writing the test. This isn't Test Driven Development.

Technically, Assert.fail() shouldn't be needed if you're using test driven development correctly.

Have you thought of using a Todo List, or applying a GTD methodology to your work?

It was deliberately left out. This is Brad Wilson's reply as to why is there no 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.

For this scenario, rather than calling Assert.Fail, I do the following (in C# / NUnit)

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

It is more explicit than an Assert.Fail.

There seems to be general agreement that it is preferable to use more explicit assertions than Assert.Fail(). Most frameworks have to include it though because they don't offer a better alternative. For example, NUnit (and others) provide an ExpectedExceptionAttribute to test that some code throws a particular class of exception. However in order to test that a property on the exception is set to a particular value, one cannot use it. Instead you have to resort to 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);
}
}

The xUnit.Net method Assert.Throws makes this a lot neater without requiring an Assert.Fail method. By not including an Assert.Fail() method xUnit.Net encourages developers to find and use more explicit alternatives, and to support the creation of new assertions where necessary.

MS Test has Assert.Fail() but it also has Assert.Inconclusive(). I think that the most appropriate use for Assert.Fail() is if you have some in-line logic that would be awkward to put in an assertion, although I can't even think of any good examples. For the most part, if the test framework supports something other than Assert.Fail() then use that.

Personally I have no problem with using a test suite as a todo list like this as long as you eventually get around to writing the test before you implement the code to pass.

Having said that, I used to use this approach myself, although now I'm finding that doing so leads me down a path of writing too many tests upfront, which in a weird way is like the reverse problem of not writing tests at all: you end up making decisions about design a little too early IMHO.

Incidentally in MSTest, the standard Test template uses Assert.Inconclusive at the end of its samples.

AFAIK the xUnit.NET framework is intended to be extremely lightweight and yes they did cut Fail deliberately, to encourage the developer to use an explicit failure condition.

I've always used Assert.Fail() for handling cases where you've detected that a test should fail through logic beyond simple value comparison. As an example:

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

Thus the lack of Assert.Fail() in the framework looks like a mistake to me. I'd suggest patching the Assert class to include a Fail() method, and then submitting the patch to the framework developers, along with your reasoning for adding it.

As for your practice of creating tests that intentionally fail in your workspace, to remind yourself to implement them before committing, that seems like a fine practice to me.

Wild guess: withholding Assert.Fail is intended to stop you thinking that a good way to write test code is as a huge heap of spaghetti leading to an Assert.Fail in the bad cases. [Edit to add: other people's answers broadly confirm this, but with quotations]

Since that's not what you're doing, it's possible that xUnit.Net is being over-protective.

Or maybe they just think it's so rare and so unorthogonal as to be unnecessary.

I prefer to implement a function called ThisCodeHasNotBeenWrittenYet (actually something shorter, for ease of typing). Can't communicate intention more clearly than that, and you have a precise search term.

Whether that fails, or is not implemented (to provoke a linker error), or is a macro that doesn't compile, can be changed to suit your current preference. For instance when you want to run something that is finished, you want a fail. When you're sitting down to get rid of them all, you may want a compile error.

With the good code I usually do:

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

With the test code I usually do:

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

If using JUnit, and don't want to get the failure, but the error, then I usually do:

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

Why would you use Assert.Fail for saying that an exception should be thrown? That is unnecessary. Why not just use the ExpectedException attribute?

This is the pattern that I use when writting a test for code that I want to throw an exception by design:

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

IMHO this is a better way of testing for exceptions over using Assert.Fail(). The reason for this is that not only do I test for an exception being thrown at all but I also test for the exception type. I realise that this is similar to the answer from Matt Howells but IMHO using the finally block is more robust.

Obviously it would still be possible to include other Assert methods to test the exceptions input string etc. I would be grateful for your comments and views on my pattern.

Beware Assert.Fail and its corrupting influence to make developers write silly or broken tests. For example:

[TestMethod]
public void TestWork()
{
try {
Work();
}
catch {
Assert.Fail();
}
}

This is silly, because the try-catch is redundant. A test fails if it throws an exception.

Also

[TestMethod]
public void TestDivide()
{
try {
Divide(5,0);
Assert.Fail();
} catch { }
}

This is broken, the test will always pass whatever the outcome of the Divide function. Again, a test fails if and only if it throws an exception.

I think you should ask yourselves what (upfront) testing should do.

First, you write a (set of) test without implmentation. Maybe, also the rainy day scenarios.

All those tests must fail, to be correct tests: So you want to achieve two things: 1) Verify that your implementation is correct; 2) Verify that your unit tests are correct.

Now, if you do upfront TDD, you want to execute all your tests, also, the NYI parts. The result of your total test run passes if: 1) All implemented stuff succeeds 2) All NYI stuff fails

After all, it would be a unit test ommision if your unit tests succeeds whilst there is no implementation, isnt it?

You want to end up with something of a mail of your continous integration test that checks all implemented and not implemented code, and is sent if any implemented code fails, or any not implemented code succeeds. Both are undesired results.

Just write an [ignore] tests wont do the job. Neither, an asserts that stops an the first assert failure, not running other tests lines in the test.

Now, how to acheive this then? I think it requires some more advanced organisation of your testing. And it requires some other mechanism then asserts to achieve these goals.

I think you have to split up your tests and create some tests that completly run but must fail, and vice versa.

Ideas are to split your tests over multiple assemblies, use grouping of tests (ordered tests in mstest may do the job).

Still, a CI build that mails if not all tests in the NYI department fail is not easy and straight-forward.

This is our use case for Assert.Fail().

One important goal for our Unit tests is that they don't touch the database.

Sometimes mocking doesn't happen properly, or application code is modified and a database call is inadvertently made.

This can be quite deep in the call stack. The exception may be caught so it won't bubble up, or because the tests are running initially with a database the call will work.

What we've done is add a config value to the unit test project so that when the database connection is first requested we can call Assert.Fail("Database accessed");

Assert.Fail() acts globally, even in different libraries. This therefore acts as a catch-all for all of the unit tests.

If any one of them hits the database in a unit test project then they will fail.

We therefore fail fast.