In previous post, I defined a good unit test as one that:

  • tests single unit of work
  • contains only one assert
  • is self contained

Presented sample of code with one unit test, unfortunately had two asserts. Clear violation of the second rule. One of the fellow bloggers MJ pointed out this mistake. It is time to “fix” it and talk about “Only one assert per test” rule.

Why only One Assert

There are couple of problems with multiple asserts.

Unit Test Misinformation

Frameworks like NUnit do notify about failing unit test when one of the asserts is not met. You get the message that some condition failed, but only this one condition. Code, behind the failing assert, is not executed. In a scenario with multiple asserts, when the first one fails, test procedure is lost and potential problem looks like related to the first assertion check. This is a misinformation.

[Test]
public void GenerateDocumentNumber_when_new_document_then_contains_string_new_and_id()
{
  // Arrange
  var newString = "new";
  var id = 10;

  // Act
  var documentNumber = generator.GenerateDocumentNumber(id);

  // Assert
  Assert.That(documentNumber, Is.StringContaining(newString));
  Assert.That(documentNumber, Is.StringContaining(id));
}

Presented example tests GenerateDocumentNumber function. This function creates simple string based on provided ID.

We want to check if :

  • document number contains 'id' value
  • document number contains keyword 'new'

If procedure fails on newString assertion we know that one part of the algorithm doesnt work. The problem is that check for ID wasn’t done at all. We don’t know if ID is set correctly. Test like this, with multiple asserts, can’t be trusted.

Complicated Tests

There is another problem with the presented unit test sample. It is too complicated. Unit test has to be focused only on one part of the algorithm. First assertion, checks if there is a ‘new’ keyword. Second, looks for ‘ID’. Both of these scenarios are great candidates for separate test.

Also, part of the code responsible for the ‘ID’, is probably more general and doesnt apply only to ‘new’ documents scenario. It is misleading, based solely on the unit test code and the name, we can assume that ‘ID’ is only added to new documents code.

We can split this test in two:

  • GenerateDocumentNumber_when_new_document_then_contains_string_new
  • GenerateDocumentNumber_contains_id_of_the_document

With one assert per unit test mindset there is better chance of creating good unit test, concentrated on one particular problem.

Going back to previous post example.

[Test]
public void If_two_chars_return_pass_and_output_coded_pass()
{
   // Arrange
   var stub = new ConsoleWrapperStub(new List
   { ConsoleKey.A, ConsoleKey.B, ConsoleKey.Enter });

   var expectedResult = "AB";
   var expectedConsoleOutput = "**";

   // Act
   var actualResult = Program.GetMaskedInput(string.Empty, stub);

   //Assert     
   Assert.That(actualResult, Is.EqualTo(expectedResult));
   Assert.That(stub.Output,Is.EqualTo(expectedConsoleOutput));
}

There are three behaviours that we have to test.

  • output '*' char for each char in provided password
  • return password
  • ConsoleKey.Enter breaks the procedure

‘Output char’ and ‘return password’ logic should be tested separaterly. The Enter key functionality is unfortunately more complicated beacuse it is mandatory in each test scenario. However still we can test some edge scenarios like “What happens when there is no Enter key ?”

One test per concept

There are of course exceptions and unit tests that could use multiple asserts.

Quote by Roy Osherove from Yeah it’s a blog

My guideline is usually that you test one logical CONCEPT per test. you can have multiple asserts on the same object. they will usually be the same concept being tested.

This rule allows multiple asserts but they have to be related and linked with simillar CONCEPT. For instance, this is not bad if multiple asserts are testing something like this.

[Test]
public void GetDocuments_returns_list_of_documents()
{
   // Act
   var documents = repository.GetDocuments();

   //Assert     
   Assert.That(documents, Is.Not.Null);
   Assert.That(documents, Is.Not.Empty);
   Assert.That(document[0], Is.Not.Null);
}

We have three asserts but they are basically testing the same thing. If one of them fails then the rest can’t be ulfilled. Fail on the first one automaticlly means that whole test failed and something bad happened. Nothing is hidden.

Good Unit Test - Summary

  • try to use one assert per test
  • if there is a need for multiple asserts, remember one test per concept rule

Links

Roy Osherove Site

Post with solution to “hide” asserts