DUnitAssertions: Are compound constraints worth it?

I’ve written enough of DUnitAssertions that you can do Expect.That(Foo, Tis.EqualTo(Bar)); You can also use and and 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 and and 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.GreaterThan and friends, for this very reason. Why was I so gung-ho about and and or?)

  • For that matter, giving people the ability to put ands and ors and 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 ands and ors and 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.EqualTo compares the value, but Tis.SameInstanceAs compares the pointer, and Tis.InstanceOf compares the class. If you combine those with and or 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 ands and ors and nots and parentheses?

  • Last but not least, ands and ors will complicate NUnit-style string comparisons. If you haven’t seen this, when you use Assert.AreEqual on 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 or to 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 and, or, and not, would you? What kinds of things might you use it for? (In other words, can you convince me this feature is worth keeping?)

Leave a Reply

Your email address will not be published. Required fields are marked *