Besides better readability (maybe?) and being able to chain constraints using &
and |
, what other advantages does the Constraint Model have over the Classic Model?
I'm a happy user of the Classic Model, and I'm in the process of deciding whether it's worth the effort to refactor old tests.
The constraint-based Assert model uses a single method of the Assert class for all assertions. The logic necessary to carry out each assertion is embedded in the constraint object passed as the second parameter to that method.
CollectionConstraint is the abstract base class for constraints that operate on collections. CollectionContainsConstraint is used to test whether a collection contains an expected object as a member.
Asserts that a condition is true. If the condition is false the method throws an AssertionException. That(Object, IResolveConstraint) Apply a constraint to an actual value, succeeding if the constraint is satisfied and throwing an assertion exception on failure.
1 of the highest class, esp. in art or literature. 2 serving as a standard or model of its kind; definitive. 3 adhering to an established set of rules or principles in the arts or sciences. a classic proof.
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.
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.
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 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With