Up until recently, I have been stuck in the past with using version 2.6.4 of NUnit. I had never upgraded because I never knew of all the benefits that I could receive by using Version 3.

One of the biggest changes in going from version 2 to 3 is that Rob, Charlie, and the other guys developing NUnit are pushing people to use the Constraint Assertion Model.

Up until now, I had only known about the Classic Assertion Model and looking back, I don't know how I lasted as long as I did without switching! In this post, I want to show you why using constraints is so much better than using classic and how it will truly change your life.

If you are like me and you write your tests, add them to your CI pipeline, passing on green, think everything is good and move on to the next project. Well, what happens when they fail?

Suddenly, a random error appears!

"Expected True but was False."

How many times have you seen this and got upset by the lack of messaging around your test failures? You wouldn't let this in your production code. Why then let it happen in your tests?

Using the constraint model, you can write your assertions, using fluent language, and have better messaging around assertions that fail so you can know exactly what is failing and why.

Classic Assertion Model

You can read about the classic model here, but these are probably the assertions that you are most familiar with and using in your tests right now.

The classic model uses individual methods for expressions on the assertion. Most commonly used methods would be Assert.AreEqual, Assert.True, and Assert.Throws.

Here is an example of a test using the classic model. In this test, I have a list of integers and I want to verify that there is exactly one int in the list that is equal to 4.

public void ClassicAssertionModel()  
    List<int> myList = new List<int> { 1, 2, 3 };
    Assert.AreEqual(1, myList.Where(x => x == 4).Count());

This particular test would fail due to the absence of 4. The output of the test would give us no information about the failure unless we opened up the code and looked at the assertion that is failing.

1) Failed : BlogSamples.Tests.ConstraintExample.ClassicAssertionModel

Expected: 1
But was: 0

We can do better than this, so let's check out constraints!

Constraint Assertion Model

With the constraint model (which you can read all about here.), you move the expressions from the method to something you pass into the assertion. This makes it so that you aren't just stuck with the builtin assertion methods and is what makes the constraint model become powerful. (In a later post, I will go into building out custom constraints, but for now I will keep it simple.)

With constraint assertions, you use one assertion method Assert.That.

So if we take our test from before and refactor it to use a constraint, it would look like this.

public void ConstraintAssertionModel()  
    List<int> myList = new List<int> { 1, 2, 3 };
    Assert.That(myList, Has.Exactly(1).EqualTo(4));

The assertion now reads fluently and doesn't require a description of what it is checking. "Verify that my list has exactly one int equal to 4."

Already, our test reads much better with constraints than it did with using classic, but what does our output look like?

2) Failed : BlogSamples.Tests.ConstraintExample.ConstraintAssertionModel

Expected: exactly one item equal to 4
But was: < 1, 2, 3 >

Boom! It tells you exactly what the assertion is looking for and what the value is that you are checking.

Already by using the constraint model, our tests are much better off. If you got this error on a build server, you would know exactly what is happening. You haven't even started using this model and you can already feel your life changing, right??

Moving forward in all my tests, I will be only using the constraint model and not even
think twice about going back. What is your excuse for still using the classic model?

Let me know your opinions on what model you think is better!

© 2022. All Rights Reserved.

Proudly published with Ghost