I get this unit testing question often. It makes efficient sense to stuff all kinds of assertions after a single setup and invocation. While there’s no one answer, for me, it usually comes down to: Try to separate rather than join them. While it’s possible to have some asserts for the same operation, in most situations, we’re better off separating them to multiple unit tests.

The main idea behind this is that if the first assertion fails, we won’t know what happened with consequent ones (depending on default implementation of unit testing frameworks). When that happens, we lose information that is important for solving the problem.

The following example here is of a transaction that transfers money from one account to another. To check it completely, we’d like to test that both account balances changed.

@Test
public void getBalance_AfterTransfer_CorrectAmount() {
    bankAccount accountSrc = new bankAccount(5000);
    bankAccount accountTarget = new bankAccount(0);
        
    accountSrc.Transfer(500, accountTarget);

    assertEquals(accountSrc.getBalance(), 4500);
    assertEquals(accountTarget.getBalance(), 500);
}

I think we can all agree that the transaction we check is a single operation. It makes sense that we assert both balance amounts here, with the same setup and invocation. One unit test to rule them all.

When You First Fail

What happens when the first assert fails? It throws an exception, the unit test ends, and the second assertion will not be checked. An error will only be reported on the first one.

The other assert could have given us information about the problem. We lost that information.

Now consider the other option. If there were two separate unit tests:

@Test
public void getBalance_AfterTransfer_SrcDecremented() {
   bankAccount accountSrc = new bankAccount(5000);
   bankAccount accountTarget = new bankAccount(0);
        
   accountSrc.Transfer(500, accountTarget);

   assertEquals(accountSrc.getBalance(), 4500);
}

@Test
public void getBalance_AfterTransfer_TargetIncremented(){
   bankAccount accountSrc = new bankAccount(5000);
   bankAccount accountTarget = new bankAccount(0);
        
   accountSrc.Transfer(500, accountTarget);

   assertEquals(accountTarget.getBalance(), 500);
}

If we ignore the duplication for a minute, now we have the results of both tests and conditions. In the unit test run report we’ll see the result of both.

Why is that important?

Because we want to fix the problem as quickly as possible. And more information early (as in the unit test report) helps you triangulate the problematic code.

Is it worth splitting the tests? As always – it depends.

Assuming we should write the test, it unit tests for a higher risk code. If that’s the case, when it breaks, we want to fix it as quickly as possible.

We’ll want more data sooner.  I’d go for the split.

 

Categories: Writing Tests

4 Comments

splintor · July 6, 2015 at 7:01 am

But what about the duplication?

Also note that this is not relevant when testing JavaScript code with Jasmine. In Jasmine, when an expectation fails, it issues an error, but the test continues to run the remaining expectations. This turns out to be quite handy, and the C# community should consider having such an option for its tests. as well.
See https://github.com/jasmine/jasmine/issues/577

Matt Griscom · August 16, 2016 at 1:52 pm

Better: Do clustered target verifications. No duplication needed. This is part of the Atomic Check pattern of the MetaAutomation pattern language.

    Gil Zilberfeld · August 17, 2016 at 5:32 pm

    Better- depending on context 🙂

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *