简体   繁体   中英

Are multiple asserts bad in a unit test? Even if chaining?

Is there anything wrong with checking so many things in this unit test?:

ActualModel = ActualResult.AssertViewRendered()        // check 1
                          .ForView("Index")            // check 2
                          .WithViewData<List<Page>>(); // check 3

CollectionAssert.AreEqual(Expected, ActualModel);      // check 4

The primary goals of this test are to verify the right view is returned (check 2) and it contains the right data (check 4).

Would I gain anything by splitting this into multiple tests? I'm all about doing things right, but I'm not going to split things up if it doesn't have practical value.

I'm pretty new to unit testing, so be gentle.

As others have pointed out, it's best to stick with one assert in each test in order to avoid losing information - if the first assert fails, you don't know whether the later ones would fail also. You have to fix the problem with less information - which might (probably will) be harder.

A very good reference is Roy Osherove's Art of Unit Testing - if you want to get started right with Unit Testing, you could do a lot worse than starting here.

Noting for future readers that this question and its duplicate Is it bad practice to have more than one assertion in a unit test? have opposite majority opinions, so read them both and decide for yourself.

My experience is that the most useful quantum of testing is not the assertion, but the scenario -- that is, there should be one unit test for a given set of initial conditions and method call, with as many assertions as are necessary to assert the expected final conditions. Having one unit test per assertion leads to duplicate setup or tortuous workarounds to avoid the duplication (such as the awful deeply nested rspec contexts that I'm seeing more and more of lately). It also multiplies tests, drastically slowing your suite.

As long as each assertion has a unique and identifying failure message you should be good and will sidestep any Assertion Roulette issues because it won't be hard to tell which test failed. Use common sense.

I have found a different argument to this question (at least for myself):

Use of multiple asserts is OK if they are testing the same thing.

For example, it's OK to do:

Assert.IsNotNull(value);
Assert.AreEqual(0, value.Count);

Why? - because these two asserts are not hiding the intention of the test. If the first assert fails, it means the second would fail too. And indeed, if we remove the first assert, the second assert fails (with null reference exception - !!!) anyway when the value is null. If this was not the case, then we should not put these two asserts together.

So, this is wrong:

Assert.IsNotNull(value1);
Assert.IsNotNull(value2);

As I've mentioned above, if the first assert fails, there is no clear indication about the second assert - we would still want to know what happens to the second one ( even if the first one failed ). So, for this reason, these two asserts belong to two different unit tests.

Conclusion: putting one or more asserts, when done properly , becomes the matter of the preference - whether we want to see assertion exceptions in the test results, or do we want to see some other exceptions as well in particular cases.

It's best to stick with only one assert in each test to avoid Assertion Roulette .

If you need to set up the same scenario to test multiple conditions based on identical premises, it's better to extract the setup code to a shared helper method, and then write several tests that call this helper method.

This ensures that each test case tests only one thing.

As always, there are exceptions to this rule, but since you are new to unit testing, I would recommend that you stick with the one assertion per unit test rule until you have learned when it's okay to deviate.

I know that this is an old question but I thought I'd add my bit.

Generally I'd have as few assertions as possible in each test case. Tests can be often be written to save having lots of different assertions.

Suppose I have unit tests for a method which creates a salutation (eg Mr Smith) from the components of a name. I want to check this with a large number of different scenarios, it is unnecessary to have a separate test for each.

Given the following code, there are many different assertions. When they fail you can fix them one at a time until the assertions stop.

Assert.AreEqual("Mr Smith", GetSalutation("Mr", "J", "Smith"));
Assert.AreEqual("Mr Smith", GetSalutation("Mr", "John", "Smith"));
Assert.AreEqual("Sir/Madam", GetSalutation("", "John", "Smith"));
Assert.AreEqual("Sir/Madam", GetSalutation("", "J", "Smith"));

An alternative to this is to keep a count of issues and assert this at the end.

int errorCount = 0;
string result;

result = GetSalutation("Mr", "J", "Smith");
if (result == "Mr Smith")
    errorCount++;

result = GetSalutation("Mr", "John", "Smith");
if (result == "Mr Smith")
    errorCount++;

Assert.AreEqual(0, errorCount);

In a real world situation I'd probably add some Trace commands to write the detail of the individual tests which failed to the output window

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM