-
Notifications
You must be signed in to change notification settings - Fork 128
A tutorial introduction for clojure.test users
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
(is (= 2
(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:
(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:
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.)
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:
However, let's move on to what you can do with facts.