Joe White’s Blog

Life, .NET, and Cats


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?)

2 Responses to “DUnitAssertions: Are compound constraints worth it?”

  1. Brad White Says:

    Seems like AND and BETWEEN are both mixing two tests. If you split it into two tests, anything that failed the compound test would also fail one of the two tests.

    OR seems harder to replace, but

    a) might be able to work out something with NOT

    b) I can’t yet think of a place i’d *need* it.

    Conversely, if you need OR, then you probably need AND also.

    " A OR (B AND C)"

    If you can’t split the OR, then you can’t split the AND either.

  2. Joe White Says:

    That’s true, anything with "between" or "and" could be two separate tests. At one point it seemed like it would be nicer if you could write a single Expect.That(…) call with a custom-rolled constraint, rather than having to make two Expect.That(…) calls (or making a custom assertion message that calls Expect.That(…) twice). But I’m having trouble nailing down an example of where it would gain you much. "Between" is the best I can come up with, and it’s not that compelling. (Heck, if "between" is that useful, I could just build Tis.Between into the framework.)

    I still haven’t come up with an example where "or" would be useful; it just seemed like a good idea at the time. Less so, the more time I spend on the idea.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>


Joe White's Blog copyright © 2004-2008. Portions of the site layout use Yahoo! YUI Reset, Fonts, and Grids.
Proudly powered by WordPress. Entries (RSS) and Comments (RSS).