Introduction

The goal of unit testing is to write comprehensive test cases and for all of these test cases to pass. To ensure the application code behaves as expected, we use assertions. An assertion is a statement that compares an actual value with the expected value. The result of the comparison is either a pass, fail, or ignore.

Inputs into assertions

Most assertions are overloaded to accept a variety of parameters. However, the two most common parameters are the expected and actual parameters. An expected parameter is the value that defines whether an assertion passes. The actual value results from invoking the code we are testing in the test case. In formal terms, the actual value is the result of acting on arranged data. Some assertions only take actual parameters with the expected parameters defined in the assertion statement, although these are rare.

Results of assertions vs. result of test cases

A test case passes if all its assertions return true. If one or more assertions return false, the whole test case fails.

Two types of assertions

NUnit provides two assertion models— classic assertions and constraint assertions. The difference between the two are summarized below:

  • Classic assertions: They are called classic assertions, because they were the exclusive assertion model before version 3 of NUnit. Classic assertions use individual methods as opposed to chaining component methods to express an assertion.

  • Constraint assertions: With constraint assertions, assertions are built by chaining various components. Since the pieces are chained, they mimic the pattern of spoken sentences. For this reason, they are also known as fluent assertions.

Benefits of constraint over classic assertions

The primary benefit of constraint assertions is that the assertion statement mimics spoken language. The second benefit is that if a constraint assertion fails, it is easier to spot why the assertion failed by just looking at the test explorer output. These benefits are demonstrated below.

Example of a classic assertion model

In the following example, suppose we want to check whether a list contains exactly one item of the value, 8. In the classic model, we would express this as follows:

Press + to interact
[Test]
public void classic_assertion_model()
{
List<int> myList = new List<int> { 2, 4, 6 };
Assert.AreEqual(1, myList.Where(x => x == 8).Count());
}

The output of running this test is as follows:

Expected: 1
But was: 0

Example of a constraint assertion

In the following example, suppose we would still like to check whether a list contains the value, 8. In the constraints model, we would include the following:

Press + to interact
[Test]
public void constraint_assertion_model()
{
List<int> myList = new List<int> { 2, 4, 6 };
Assert.That(myList, Has.Exactly(1).EqualTo(8));
}

The output of running this test is as follows:

Expected: exactly one item equal to 8
But was: < 2, 4, 6 >

From this example, we see that not only do constraint assertions read more fluently, but they are also more easily interpreted if the assertion fails.

Adding comments to output failed assertions

​​We should aim to include messages as a part of the assertions. A message may be displayed as the output of a test if an assertion fails.

Press + to interact
[Test]
public void withdraw_money()
{
//Arrange
BankAccount account = account = new BankAccount(1000);
//Act
account.Withdraw(300);
//Assert
Assert.AreEqual(700, account.Balance, "The balance after withdrawing money is incorrect");
}

If the assertion fails, the output of running the test is as follows:

The balance after withdrawing money is incorrect
Expected: 700
But was:  XXX

Note: XXX is the result that is not equal to 700.

From the example above, we can see that if a test fails, messages provide context to the failing test. This expedites the process of correcting the application code.

Code playground

The widget below contains the contents of the code snippets above. Run the tests below to confirm the outcomes of the code snippets above:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Project
{
    public class BankAccount
    {
        public decimal Balance { get; set; }

        public BankAccount(decimal initialAmount)
        {
            Balance = initialAmount;
        }
        public void Withdraw(decimal amount)
        {
            Balance -= amount;
        }
    }
}
Classic vs. constraint assertions

Conclusion

Assertions are the core of any unit test project. They are utilized as a classic model or a constraint model. Both models are acceptable for use. Messages should be included with assertions to enhance the test code documentation if an assertion fails.