I’ve written enough of DUnitAssertions that you can do
Expect.That(Foo, Tis.EqualTo(Bar)); You can also use
or to combine multiple
Tis constraints, so you could write an assertion like:
Expect.That(Foo, Tis.GreaterThan(20) and Tis.LessThan(45));
This kind of
or stuff seemed like a great idea. You could write your own
TisBetween method, implement it in terms of existing constraints, and start using it:
Expect.That(Foo, TisBetween(20, 45)); And you would just magically get a meaningful failure message like:
Expected: > 20 and < 45 but was: 99
Well, it did sound cool at one point. But the more I work on it, the more problems I run into.
To start with, “between”, my prime example, is a bad example. “Between” is just a bad test. It’s sloppy, kind of like an expected-exception check that passes even if a descendant exception is thrown. You should know what the value is supposed to be, and that’s what your test should check for. Using something like “between” means you could introduce an off-by-one error in a botched refactoring, and your tests wouldn’t tell you about it. (I even hesitated before I added
Tis.GreaterThanand friends, for this very reason. Why was I so gung-ho about
For that matter, giving people the ability to put
nots into their tests is just begging the less-unit-test-savvy among them to rewrite their program logic in their tests. That kind of duplication is the last thing the programming world needs. This alone isn’t an argument against the feature, but it’s a point to consider.
What should the failure message look like when you have a complicated expression with
nots? How do you visually represent that in the failure message? You’d need to start adding parentheses to show precedence, and that doesn’t fit with the one-condition-per-line layout. Things would get messy in a hurry.
What happens when you get a tug-of-war over what goes into the “but was:” line?
Tis.EqualTocompares the value, but
Tis.SameInstanceAscompares the pointer, and
Tis.InstanceOfcompares the class. If you combine those with
or, which one shows after “but was:”? Do there need to be multiple paragraphs in the output, each with its own “but was:” line? How does that interact with multiple
nots and parentheses?
Last but not least,
ors will complicate NUnit-style string comparisons. If you haven’t seen this, when you use
Assert.AreEqualon two strings, NUnit will find the first index where the strings differ, and will show you something like twenty characters of context on either side, with an arrow pointing to the first character that’s different. It’s really sweet, and I want it for DUnitAssertions… but how does it coexist with, say, the ability to use
orto compare against multiple different strings? Which one controls the arrow?
I have support for
Tis.Not, so you can do things like
Tis.Not.Null, and you could use
Tis.Not.GreaterThan in those cases where it would increase readability. But beyond that, I’m debating how valuable operator overloads are for constraints, or whether I should just rip them out.
So here’s my question to y’all: If you could combine constraints with
not, would you? What kinds of things might you use it for? (In other words, can you convince me this feature is worth keeping?)