Slightly Less Typed Assertions

A couple of weeks ago I wrote about typed assertions for Lazytest.

Like so many things, it seemed like a good idea at the time. Define typed objects for each kind of assertion (e.g., equality, instanceof). When a test fails, throw an exception with one such object as the payload. The type of the object describes the failure (e.g., not equal, not instance of) and its fields describe the components of the failure (e.g., X was not equal to Y).

It worked, but as Rich Hickey himself pointed out, it was redundant. All these typed objects have no methods, just different kinds of fields. Furthermore, most of the fields aren’t even that different. Every “failure” can be identified by a predicate and the arguments which caused it to return false.

I should have known something was wrong as soon as I wrote a Protocol with no methods. I was doing JavaBean-style programming, using objects to represent structured data. I can represent them more simply as maps.

So that’s what I’ve done. The strongly typed failures are gone. The complex code transformation and replacement in the expect macro are gone.

What’s left is a much shorter form of the expect macro that examines the assertion expression and, if it contains a normal function call, evaluates its arguments before calling the function. Effectively, it follows the same evaluation rules as Clojure itself, while holding on to the intermediate values. A failing test throws an exception with an attached map holding the original unevaluated form, the evaluated components, and the return value of the predicate.

1 thought on “Slightly Less Typed Assertions”

  1. it’s been interesting to follow the design decisions.

    Is there any way to bind doc metadata to expect for reporting or have nested do-it calls?
    I have multiple expects inside same do-it (as a way to compose expectations), but not sure if there’s a better around this.

    thanks.

Comments are closed.