Assertions
Assertions are central to unit testing in any of the xUnit frameworks, and NUnit is no exception. NUnit provides a rich set of assertions as static methods of the Assert class.
If an assertion fails, the method call does not return, and an error is reported. If a test contains multiple assertions, any that follow the one that failed will not be executed. For this reason, it's usually best to try for one assertion per test.
NUnit supports two models to write assertions: Constraint model and Classic model. In the latest versions the Classic model has been reimplemented using the newer Constraint model and will not receive new features. For this reason, we will focus on the Constraint model.
The Constraint model is built around the statement Assert.That(actualValue, constraint)
where constraint is an object implementing the IResolveConstraint
interface. Furthermore, NUnit provides many helper methods aiming at creating a fluent expression.
Here are some examples of assertions builts using the constraints model
Assert.That(result, Is.EqualTo(42))
Assert.That(result, Is.GreaterThan(0))
Assert.That(result, Is.Not.Null)
Assert.That(result, Is.False)
That
supports multiple overloads that allow to specify the message to be returned in case of failure and more.
Here is a list of all built-in constraints available in NUnit.
Comparing values
Asserting equality between two values is less intuitive than one would think because several aspects need to be taken in consideration.
By default, the EqualConstraint
uses the closest override of the Object.Equals
method.
When comparing numeric types, developers can use the methods Within
to specify the tolerance, both in absolute and relative terms. Comparison constraints like GreaterThan
, GreaterOrEqualThan
, LessThan
and LessOrEqualThan
also expose the Within
methods.
When comparing strings, developers can use IgnoreCase
to perform a case-insensitive comparison.
If that is not enough, the developer can specify how the equality check is performed by using the method Using to specify a comparer.
The comparer can be
An instance of a class implementing
IEquatableComparer
orIEquatableComparer<T>
An instance of a class implementing
IComparer
orIComparer<T>
An instance of a class inheriting from
Comparison<T>
A
Func<T, T, bool>
delegate
Here is the official documentation regarding the EqualConstraint.
Finally, the SameAsConstraint
checks that the two variables are referencing the same object.
Here is the official documentation regarding the SameAsConstraint.
Expecting exceptions
Sometimes we expect our code to throw an exception under given conditions.
As for normal assertions, NUnit provides several utilities to test whether the system under test throws an exception.
Following the Constraint model showed earlier, developers can use overloads of the That
method to assert the expected behavior.
The two basic overloads that will be used in this scenario are:
That(TestDelegate, IResolveConstraint)
That(AsyncTestDelegate, IResolveConstraint)
TestDelegate
and AsyncTestDelegate
simply represent any synchronous and asynchronous action.
Additionally, NUnit provides helper methods aiming at describing the expected exception in a fluent manner. Here are some examples:
Assert.That(() => sut.DoSomething(null), Throws.ArgumentNullException)
Assert.That(() => sut.DoSomething(""), Throws.ArgumentException)
Assert.That(() => sut.DoSomethingElseAsync(), Throws.TypeOf<MyCustomException>())
Combining assertions
Often, the state of the system under test cannot be described in a single statement.
NUnit offers some alternatives to handle this scenario: they mostly overlap but some might not always be applicable and others might not be optimal. The first option is to stack several calls to the Assert utilities, one after the other.
This option is the most intuitive one but its main drawback is that if the first assertion fails, the subsequent ones will not be executed.
The second option is to combine multiple constraints into one using logical operators
The same can also be achieved using the fluent syntax helpers
The problem with this approach is that the combined constraints can only target the same "actual value". There are ways to explore properties of an object by passing the name of the property as string. Unfortunately, using strings to navigate properties isn't the most optimal approach as there is no check from the compiler and causing the test to fail if the properties were to be renamed in the future.
The third option comes in help in this case. By leveraging Assert.Multiple
, developers can cluster together multiple assertions with the guarantee that all will be executed even if any of them were to fail.
Here is the official documentation regarding this option.
Evaluating collections
Sometimes, tests need to assert the state of collections of items. NUnit includes constraints that help dealing with collections and their items.
When comparing two collections, the following scenarios are supported
The two collections must contain the same elements in the same order:
Assert.That(actual, Is.EqualTo(expected))
The two collections must contain the same elements in any order:
Assert.That(actual, Is.EquivalentTo(expected))
One collection is a subset of the other:
Assert.That(actual, Is.SubsetOf(expected))
One collection is a superset of the other:
Assert.That(actual, Is.SupersetOf(expected))
The tested collection is empty:
Assert.That(actual, Is.Empty)
Furthermore, the constraints AllItemsConstraint
, SomeItemsConstraint
and NoItemConstraint
allow to make assertions about the items of the collection.
Like for other constraints, these ones too have fluent equivalents
All items must match the condition:
Assert.That(collection, Has.All.GreaterThan(0))
At least 1 item must match the condition:
Assert.That(collection, Has.Some.GreaterThan(0))
No item must match the condition:
Assert.That(collection, Has.None.GreaterThan(0))
Exactly the specified number of items must match the condition:
Assert.That(collection, Has.Exactly(3).GreaterThan(0))
Custom constraints
Since the second parameter of That can be any object implementing the IResolveConstraint
interface, developers can create custom constraints and use them in the That
method
Assert.That(result, new MyCustomConstraint(expectedValue))
Custom constraints should be used to glue NUnit with other libraries or to test specific aspects of the domain.
Custom constraints should be avoided as a mean to combine multiple assertions.
Last updated