Skip to content
Phillip Mates edited this page Nov 9, 2017 · 14 revisions

Executable examples

Facts are the smallest form Midje checks. Here's a fact with one checkable:

(fact (+ 1 1) => 2)

Here's a slightly more complicated fact:

(fact "addition has a unit element, 0, such that (+ x 0) => x"
  (+ 0 0) => 0
  (+ 1 0) => 1
  (+ -1 0) => -1
  (+ 100000 0) => 100000)

The doc string makes a universal claim, which Midje cannot verify. It can, however, evaluate and check the four checkables. Seeing them succeed will give you confidence in the claim (at least until you learn about arithmetic overflow).

In this user guide, fact doc strings are referred to as "descriptions", because they're stored in fact metadata under the key :midje/description.

Fact descriptions are used to create failure messages:

user=> (fact "Twice two is three"
         (* 2 2) => 3)
FAIL "Twice two is three" at (NO_SOURCE_PATH:2)
    Expected: 3
      Actual: 4
false

Notice that the result of the fact is false. The result is true only if all predictions come true.

Nesting

Checkables don't have to be at the top level. Here's a checkable nested inside a let:

(fact
  (let [expected 2]
     (+ 1 1) => expected
     (* 2 1) => expected
     (- 3 1) => expected))

Notice that the checkables can use lexically scoped symbols.

Facts can be nested arbitrarily deeply:

user=> (facts "about arithmetic" 
         (fact "there is addition"
           (+ 1 1) => 2)
         (fact "about subtraction"
           (- 1 1) => 0)
         ;; Couldn't think of a fact name for this...
         1 => pos?
         -1 => neg?)
true

Notice that the outermost structure was named facts. That's not required: it's just a synonym for fact.

All the nested descriptions are used to create the failure output:

user=> (facts "about arithmetic"
         (fact "twice two is three"
           (+ 2 2) => 3))

FAIL "about arithmetic - twice two is three" at (NO_SOURCE_PATH:3)

Tabular facts

Facts can contain tables. That looks like this:

(tabular "ways to arrive at 2"
  (fact
    (?op ?left ?right) => 2)
  ?op ?left ?right
   +    1     1
   *    1     2
   /    4     2)
true

That form has three parts: the symbol tabular (and optional description), an ordinary fact with particular symbols marked as special, and a table that gives values to substitute for special symbols.

The above tabular fact is actually expanded into something like this form:

(fact "ways to arrive at 2"
  (fact
    (+ 1 1) => 2)
  (fact
    (* 1 2) => 2)
  (fact
    (/ 4 2) => 2))

While that's not wildly efficient, it allows much of the power of macroexpansion.

The hedging above ("something like this form") is because failure messages provide more information about what substitution was made than a simple expansion would.

Generative facts

Generative tests that use facts can created using the for-all construct (added in 1.9.0-alpha11)

(require  '[clojure.test.check.generators :as gen])

(defn my-keys [a-map] (map second a-map))

(for-all 
  [str-map (gen/map gen/keyword gen/string)]
  {:max-size 10
   :num-tests 15
   :seed 1510160943861}
  (fact "extracted keys are strings"
    (my-keys str-map) => (has every? string?))
  (fact "my-keys matches keys behavior"
    (my-keys str-map) => (keys str-map)))

The form has three parts: the symbol for-all (and optional description and options map), the binding vector where symbols are bound to example values generated from the generators, and the body, which should contain one or more fact expressions.

More info here

Fact metadata

Facts can have metadata. It's described completely in syntax and a little semantics. As an example, though, here's metadata tagging a slow test:

(fact "check Takeuchi's number" :slow ...)

Both lein-midje and the repl tools can be told to skip :slow tests (or to run only slow tests). See using metadata to filter facts for more.

Future facts

Sometimes you want to use a fact as a "todo". That can be written like this:

user=> (future-fact (time np) => polynomial?)

WORK TO DO at (NO_SOURCE_PATH:1)

If the fact has a description, that will also be printed.

Clone this wiki locally