Skip to content

A tutorial introduction for clojure.test users

marick edited this page Feb 8, 2013 · 47 revisions

This tutorial shows a clojure.test user how to migrate to Midje. That migration can be gradual: Midje coexists with clojure.test, so you can use both at the same time, even in the same file.

Start by getting a small project to work on:

% git clone git@github.com:marick/midje-clojure-test-tutorial.git

The project is about a whimsical little function called migrate that "moves" key/value pairs from one map to another. Migrate calls are written left to right: (migrate source-map key-to-migrate destination-map), and the result is the "changed" [source, destination] pair after the migration:

user=> (migrate {:a 1} :a {})
[{} {:a 1}]

You can migrate more than one key:

user=> (migrate {:a 1, :b 2, :c 3} :b :c {})
[{:a 1} {:b 2, :c 3}]

In the case of key clashes, the migration isn't done:

user=> (migrate {:a 1, :b 2} :a :b {:a "not-1"})
[{:a 1} {:b 2, :a "not-1"}]

migrate has tests. Here are the first two of them:

(deftest migration
  (testing "duplicates are not migrated"
    (is (= [{:a "not moved"} {:a "retained"}]
           (migrate {:a "not moved"} :a {:a "retained"})))))

To do the equivalent in Midje, you need to add Midje to your project.clj file:

(project 
  :dependencies ...             ;; typically, :dev dependencies
                [midje "1.5.0"]
                ... )

Then use midje.sweet in your namespace:

  (:use clojure.test
        midje.sweet       ;; <<<<  
        migration.core))

Then you can write Midje code that looks structurally very like the clojure.test tests, except for different jargon:

(facts migration
  (fact "migration produces two maps with keys (and values) from one moved to the other"
    (migrate {:a 1} :a {}) => [{} {:a 1}])
  (fact "duplicates are not migrated"
    (migrate {:a "not moved"} :a {:a "retained"}) => [{:a "not moved"} {:a "retained"}]))

The most obvious difference is that the is expressions have been replaced by ones modeled after the way everyone writes examples in documentation: source on the left, some sort of delimiter (like an arrow or newline or comment symbol or repl prompt) to separate the source from the result, and then the result. Flip open a book on Clojure, and I expect you'll see examples like that. The delimiters make them easier to read, and they follow the conventions readers of left-to-right languages expect (particularly that time flows from left to right and top to bottom).

A lot of the structure you see above is optional. For example, here's a minimalist version:

(fact
  (migrate {:a 1} :a {}) => [{} {:a 1}]
  (migrate {:a "not moved"} :a {:a "retained"}) => [{:a "not moved"} {:a "retained"}])

When you run the tests with lein tests, midje failures print out:

A midje failure message in lein test output

(I used a screen shot here to emphasize that Midje by default uses terminal colors in its output.)

Unfortunately, Midje's error is not included in the clojure.test's error counts. For that reason, I recommend you use Midje's own Leiningen plugin. To install it, add this to your :user profile in ${HOME}/.lein/profiles.clj:

{:plugins [... [lein-midje "3.0"] ...]}

Now you can do this:

Lein midje output

Notice that both Midje and clojure.test output are reported (and colored so that failures stand out). Both Midje's and clojure.test's failure counts are reported. They are also both used to construct the exit status, which is 0 if there were no problems, a non-zero number otherwise. (Strictly, the exit status is the number of failures, up to 255. So in this case, the exit status is 2.)

Because Clojure and Midje's startup time is slow, you will probably prefer to use "autotest", in which Midje watches your project for changed files. When it sees a change, it reloads the changed files and all files that depend on it. In the following example, I start autotest on a buggy version of migrate, make (and save) a syntax error trying to fix it, and then fix it for real. (I've removed the clojure.test tests to keep the output from flooding the screen.)

Lein midje output

I personally prefer to start autotest from within the repl, using Midje's Repl Tools. That makes it easy to fluidly switch between test-driven development and repl-driven development:

Lein midje output

However, let's move on to what you can do with facts.

Clone this wiki locally