55

Is it bad practice to have more than one assertion in a unit test? Does it matter?

Dave Schweisguth
  • 36,475
  • 10
  • 98
  • 121
leora
  • 188,729
  • 360
  • 878
  • 1,366
  • duplicates http://stackoverflow.com/questions/762192/unit-testing-is-this-the-right-way-or-the-wrong-way – Carl Manaster Apr 17 '09 at 23:07
  • 3
    I don't think that's a dupe, @mek. Your link is for more than one check in a test, this seems to be for things other than asserts. – paxdiablo Apr 17 '09 at 23:13
  • See also http://stackoverflow.com/questions/5025435/how-to-avoid-multiple-asserts-in-a-junit-test – Raedwald Apr 05 '16 at 11:53

8 Answers8

53

Sometimes I have exactly one assert per test case, but I think more often I have several assert statements.

I've seen the case that @Arkain eludes to, where a very large piece of code has a single unit test suite with just a few test cases, and they are all labeled testCase1, testCase2, etc, and each test case has hundreds of asserts. And even better, each condition usually depends upon the side-effects of previous execution. Whenever the build fails, invariably in such a unit test, it takes quite some time to determine where the problem was.

But the other extreme is what your question suggests: a separate test case for each possible condition. Depending on what you're testing, this might make sense, but often I have several asserts per test case.

For instance, if you wrote java.lang.Integer, you might have some cases that look like:

public void testValueOf() {
    assertEquals(1, Integer.valueOf("1").intValue());
    assertEquals(0, Integer.valueOf("0").intValue());
    assertEquals(-1, Integer.valueOf("-1").intValue());
    assertEquals(Integer.MAX_VALUE, Integer.valueOf("2147483647").intValue());
    assertEquals(Integer.MIN_VALUE, Integer.valueOf("-2147483648").intValue());
    ....
}

public void testValueOfRange() {
    assertNumberFormatException("2147483648");
    assertNumberFormatException("-2147483649");
    ...
}

public void testValueOfNotNumbers() {
    assertNumberFormatException("");
    assertNumberFormatException("notanumber");
    ...
}
private void assertNumberFormatException(String numstr) {
    try {
        int number = Integer.valueOf(numstr).intValue();
        fail("Expected NumberFormatException for string \"" + numstr +
             "\" but instead got the number " + number);
    } catch(NumberFormatException e) {
        // expected exception
    }
}

Some simple rules that I can think of off hand for how many assert's to put in a test case:

  • Don't have more than one assert that depends on the side-effects of previous execution.
  • Group asserts together that test the same function/feature or facet thereof--no need for the overhead of multiple unit test cases when it's not necessary.
  • Any of the above rules should be overridden by practicality and common sense. You probably don't want a thousand unit test cases with a single assert in each (or even several asserts) and you don't want a single test case with hundreds of assert statements.
Jared Oberhaus
  • 14,547
  • 4
  • 56
  • 55
19

No it is not a bad practice. If the method you are testing returns a class, you should test the different variables that should have been set. For this purpose you might as well use one unit test.

If, however, you are testing several features in one unit test, it won't be as clear when it fails which features caused the problem. Remember unit tests are your friend, so let them help you. Make it easily available to see what went wrong so you can go fix it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jesper Fyhr Knudsen
  • 7,802
  • 2
  • 35
  • 46
  • -1: I would test the different properties of that class with different "unit tests". That makes it VERY easy to spot errors/problems (not passed tests) – Peter Gfader May 20 '09 at 13:23
12

Your unit tests should be reasonably fine-grained. Typically, the fewer asserts, the more likely your test is to target a specific feature and not mix testing for multiple features in the same test. Does this mean that all tests should only have one assert? No, but I would consider it a "test smell" if I found several asserts, potentially testing multiple things in the same unit test. Treat this "smell" like you would a code smell and refactor the test to refine it so that it only tests one "thing" -- even if it requires more than one assert.

For example, I'm doing an MVC project now and one of the tests that I write is that the correct view is rendered by the action. There may actually be several of these if different code paths may result in different views. This is how I define it being the correct view: the result is the correct type and has the correct name. This requires two asserts, but I'm only testing one thing.

var result = controller.Action() as ViewResult;

Assert.IsNotNull( result );
Assert.AreEqual( viewName, result.ViewName );

I might do something similar with the model, but I would not test that the model is correct in the same test as checking the view because these are different aspects of the behavior of the code. I could change the expected model or view and by putting it in a separate test, only those tests concerned with that feature of the method need to be changed.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 1
    i would put those 2 asserts in 2 different unit tests. If necessary refactor the test code into a method, so that you don't have duplicate code. – Peter Gfader May 20 '09 at 13:35
  • 3
    you can remove the first assert, because if the second one assert throws nullreferenceexception, you know it's null. – xhafan Jun 01 '12 at 07:33
  • 1
    @xhafan - that won't display as well in the testing framework and seems to me to me to be a bug in the test. – tvanfosson Jun 01 '12 at 11:53
6

For me its very common to have more than one assert in a unit test. I usually have an assertion of a precondition and then an assert for the expected post condition.

Consider:

assert(list.isEmpty());

FetchValues(list);

assert(list.count == expectedItemCount);

AssertValuesMatch(list,expectedValues);

Yes, I could split up the two post conditions into two tests but depending on the cost of the FetchValues could slow down the overall test process needlessly.

MikeJ
  • 14,430
  • 21
  • 71
  • 87
  • 2
    Since the asserts are not behind one another, that's ok. But this is more an "automated functional test" then, than a "unit test". – Peter Gfader May 20 '09 at 13:30
2

I should definitely use only one assert in test method! Using many asserts may be the code smell that you are testing more than one thing. Moreover, there is a chance that somebody can add new assert into your test instead of writing another one. And how can you understand how your other asserts completed when the first one failed?

You may also found interesting this article: https://timetocode.wordpress.com/2016/06/01/zen-of-unit-testing/

Mutex
  • 430
  • 2
  • 5
  • 14
2

I don't consider it bad practice. Unit tests can do whatever they want: assert, log to files, send insulting SMS messages to management, anything.

The possible problem is that added complexity may change the behavior of the program under test but that's rarely the case if you're being careful, and can be discovered anyway.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • 1
    A "Unit test" should test 1 unit, so be as small as possible and test only 1 certain thing. I would go for only 1 assertion – Peter Gfader May 20 '09 at 13:30
  • Peter, that depends on the granularity you want. You may want a test for every possible failure (such as an integer being too high and too low as two *separate* assertions). However, reductio ad absurdum would tilt that toward an assertion for *every* possible value out of range :-) I find it more useful to have a unit test test for some less granular problem, even up to the possibility that it checks all failure modes of a given value (eg, too high, too low, not a prime as three separate assertions in a single test). I see little reason to have a massive number of different tests. – paxdiablo Sep 26 '19 at 06:16
0

Put in all the asserts in that you want. Seriously.

I try to assert every step of the way up to and including the specific goal of my test.

plinth
  • 48,267
  • 11
  • 78
  • 120
  • 2
    -1: If you have 2 assertions one behind the other, the 2nd one depends from the 1st one. Makes it very hard to find problems/errors in test cases – Peter Gfader May 20 '09 at 13:25
  • 1
    You do know that you can and should put messages in your assertions, right? That tells you exactly where the failure is. With NUnit as a test rig, you also get the stack crawl, so you know exactly where the failure is. I don't see what your issue is. – plinth May 20 '09 at 13:53
  • 2
    Careful: multiple assertions at the end of a test are fine (the majority opinion on this post) but you won't find many people who think that asserting things *before* you call the method you're testing is a good idea. Those assertions should already be in other unit tests. – Dave Schweisguth May 08 '14 at 19:37
-3

It doesn't matter. The only thing that matters is that your unit tests will cover all possible bugs.

This is an example of "over-thinking".

Tom
  • 1,636
  • 2
  • 13
  • 21
user88637
  • 11,790
  • 9
  • 37
  • 36