Custom Test Asserts in Planck

February 25, 2016

The cljs.test library provides a mechanism for writing custom asserts that can be used with the is macro—in the form of an assert-expr defmulti.

In JVM ClojureScript, any assert-expr defmethod must be written in Clojure, and you must arrange to have this Clojure code executed when running tests. This is a fallout of the fact that is is a macro written in Clojure.

But, in the bootstrapped ClojureScript port of cljs.test that ships with Planck 1.10, such defmethods can be written directly in ClojureScript (even in the REPL if you wish!) This, IMHO, makes things a bit more straightforward.

Here's an example.

Fire up the Planck REPL, passing the -s flag so that static functions are used (this is needed to cope with the underlying JavaScript generated). Issue a require to load the cljs.test library:

(require '[cljs.test :refer-macros [is]])

For our example, lets focus on the char? predicate. In ClojureScript, if you pass nil to char?, it will throw an exception (as opposed to returning false).

If you evaluate (is (char? nil)), you will see the consequence of this in the actual portion of the report, which unfortunately could be misleading:

ERROR in () (isUnicodeChar@file:269:12)
expected: (char? nil)
  actual: #object[TypeError TypeError: null is not an object (evaluating 'ch.length')]

Let's improve this by writing a specialized assertion for char?:

(defmethod cljs.test/assert-expr 'char? 
  [menv msg form]
  (let [arg    (second form)
        result (and (not (nil? arg))
                    (char? arg))]
       (if ~result
           {:type     :pass
            :message  ~msg
            :expected '~form
            :actual   (list '~'char? ~arg)})
           {:type     :fail
            :message  ~msg
            :expected '~form
            :actual   (list '~'not 
                        (list '~'char? ~arg))}))

The above is a lot like the general predicate assertion built into cljs.test, but it also includes a nil-check prior to applying char?.

With this defmethod having been evaluated at the Planck REPL, now (is (char? nil)) genreates a nicer error report:

FAIL in () (eval@[native code]:NaN:NaN)
expected: (char? nil)
  actual: (not (char? nil))
Tags: Planck ClojureScript