Advantages of the Constraint Model over the Classic Model in NUnit?
I have to say I'm a fan of the "classic" model. I find it easier to formulate my assertions for the most part, and I find them easier to read too. There's nothing in the code which isn't necessary - no "That" and "Is" which sounds fluent but doesn't lend itself to discoverability IMO.
That may well be due to familiarity as much as anything else, but I thought it would just be worth reassuring you that you're not the only one who finds the classic model perfectly reasonable :)
Having said that, when there's something which is easier to express using constraints, it makes sense to use it. This is particularly true when there's some condition which can be explicitly tested with a constraint, but where the classic model would just use Assert.IsTrue(condition)
. The key point is that the constraint is likely to be able to give you more information than the classic one for such cases.
As such I think it's a good idea to learn the constraint-based model, but I wouldn't go as far as converting any tests, and I wouldn't use it where you find the classic model to be simpler or more readable.
The NUnit 3 documentation that introduces assertions and that compares the newer Constraint Model to the Classic Model includes the following example:
For example, the following code must use the constraint model. There is no real classic equivalent.
int[] array = new int[] { 1, 2, 3 }; Assert.That(array, Has.Exactly(1).EqualTo(3)); Assert.That(array, Has.Exactly(2).GreaterThan(1)); Assert.That(array, Has.Exactly(3).LessThan(100));
While the document states that there is no "real classic equivalent," one could use Classic Syntax with LINQ to write what I would consider equivalent tests:
Assert.AreEqual(1, array.Where(x => x == 3).Count());
Assert.AreEqual(2, array.Where(x => x > 1).Count());
Assert.AreEqual(3, array.Where(x => x < 100).Count());
Some might conclude that the Constraint Model tests that I lifted from the documentation are more readable than these Classic Model equivalents. But that is arguably subjective.
However, that is not the whole story. More important is the improvement in the error message that a failed Constraint Model test emits when a test fails.† For instance, consider this Classic Model test that will fail:
int[] array = new int[] { 1, 2, 3 };
Assert.AreEqual(1, array.Where(x => x == 4).Count());
The AssertionException
that is thrown by NUnit contains the following "terse" Message
:
Expected: 1
But was: 0
In contrast, when expressing this test in the newer Constraint Model syntax:
Assert.That(array, Has.Exactly(1).EqualTo(4));
...NUnit returns the Message
:
Expected: exactly one item equal to 4
But was: < 1, 2, 3 >
I think that most would agree that this exception message is much more helpful than the one produced using NUnit's older Classic Model syntax.
† Much thanks to @nashwan for helping me understand this important improvement in the error messaging introduced in the Constraint Model.
I don't know that there are any other advantages besides readability. But that can be a big advantage. I find that simply starting every test with Assert.That( ... )
rather than having a handful of Assert functions makes it a million times easier to visually scan your asserts, since you no longer have to bother with the function name, just look at the arguments.
With NUnit 2.4, both syntaxes (class and constraint) use the exact same code underneath. There's no advantage behind the scenes one way or another. Unless you really have no better use for the time, I wouldn't bother rewriting tests.
I personally prefer the Constraint Model Assert.That
style and only use it now. I find this newer style more readable, and have determined that it is much less likely to get the "actual" and "expected" arguments mixed up. (Like you certainly can using the Classic Model Assert.AreEqual
, etc., which I have seen many people do.) This obviously leads to broken tests that report incorrect results.
As an example, without checking ;-), which of these is correct?
Assert.AreEqual(actual, expected);
Assert.AreEqual(expected, actual);