Testing Deep Structures With Midje

September 2, 2015

A Short Midje Crash Course

Here's a Midje fact:

(fact "adding two and two produces four"
  (+ 2 2) => 4)

Midje introduces the => (which I read produces): the expression to the left of => is evaluated and compared with the expression on its right, using Midje's notion of extended equality. The extended part is useful to us here:

(fact "adding two and two produces a positive number"
  (+ 2 2) => pos?)

If the result of the right side is a function, it is applied to the result of the left side to see if the test passes. In this case (pos? (+ 2 2)) is true, so the test passes.

So why not write (pos? (+ 2 2)) => true? There is a difference in what is printed when the test fails: the first prints this:

Actual result did not agree with the checking function.
        Actual result: -98
    Checking function: pos?

while the second prints:

  Expected: true
    Actual: false

The first form helps us out because it gives us more information about what happened when a test fails.

Testing Deep Structures

In Avi, the editor's state is a large, nested map containing open buffers, the cursor position, the current viewport position, the contents of the status line, the editor's mode and more.

Each time an event (usually a keystroke) is received, the editor state is advanced to the next state. For example, the editor might have a key :mode with a value of :normal, but after advancing the state by applying the ":" character, :mode might be :command-line.

The usual way to test this might be:

(fact "`:` enters command-line mode"
  (:mode (editor :after ":")) => :command-line)

(editor is a testing helper function which allows super-concise tests.)

If this test fails, we'll see:

FAIL ": enters command-line mode"

  Expected: :command-line
    Actual: :normal 
This doesn't tell us much, does it? On the other hand, we can write a mode testing function:

(defn mode
  (fn [editor]
    (= (:mode editor) expected-mode)))

and rewrite our fact:

(fact "`:` enters command-line mode"
  (editor :after ":") => (mode :command-line))

Now, when the test fails, we might be able to see, for example, that a ":" was literally inserted into the buffer–or that it wasn't.

A General Rule

I think this warrants a general rule:

When you find yourself doing gyrations on the left side of =>, write a > Midje checker instead.

It's also possible that you could be testing through a different function which produces a "more-inner" data structure – but first consider whether that function's signature is stable enough, or you will cause yourself pain later.

Tags: clojure midje