Skip to content

Metadata

marick edited this page Oct 26, 2014 · 9 revisions
Clone this wiki locally

Short version

In your source:

(fact :slow ...) ; Runs slowly
(fact "a core fact" :core)
(fact "metadata map" {:priority 5})

(fact :slow      ; all enclosed facts are also slow
  (fact ...)
  (fact ...))

(fact-group :slow ; all enclosed facts have metadata `:slow`.
  (fact ...)      ; but they can still be run independently
  (fact ...))

In the repl:

(load-facts :core)    ; Load-and-check only the core facts

(load-facts :all)                ; ":all" is not a metadata key. Loads all namespaces.
(check-facts :core)              ; Check a subset
(check-facts (complement :core)  ; Check the other facts.

With lein repl:

% lein midje :filter core -slow # select tests that are "core" or not "slow"

Details

Defining metadata

Metadata lies between a fact symbol (fact, facts, future-fact, fact-group, etc.) and the first non-metadata form. Metadata forms match ( <string> | <name> | <keyword> | <map> )*.

A map is merged onto a compiled fact's metadata.

A keyword :foo is equivalent to {:foo true}.

A string "foo" is equivalent to {:midje/description "foo"}.

A symbol foo is equivalent to {:midje/name "foo")}.

You can put any metadata you like on a fact. Certain keys are special:

:midje/description
The fact's outermost doc string, if given. It is the description that is printed when a fact fails.

:midje/name
The name of the fact. If a symbol is given in the metadata position, the string name of that symbol becomes the name. Otherwise, if a doc string is given, it becomes the name. Otherwise, there is no name. (Names are relevant to redefining facts when reloading. See the compendium.)

:midje/file, :midje/line, midje/namespace:
These identify the source of the fact. The line number is that of the beginning of the fact. The namespace is the symbol name of the enclosing namespace, not the namespace itself.

:midje/source: The original source of the fact.

:midje/guid: A unique identifier constructed from the body of the fact (excluding metadata). It's used to detect when new fact definition is the same as one that's already been seen.

Here are examples of :midje/description and :midje/name:

;; "doc string" is both the name and the description.
(fact "doc string" ...)

;; The string "cons" is the name. There is no description.
(fact cons ...)

;; The string "cons" is the name. "Cons tests" is the description.
(fact "Cons tests" cons 
  ...)

Quoting and metadata

All variants of metadata declarations follow Clojure's normal quoting rules, except that the symbol name is auto-quoted:

user=> (fact fred-fact 1 => 1)
true

Because of this, you cannot use the ordinary syntax to generate facts with computed descriptions or names. That is:

(doseq [name ["check it" "double check it" "*triple* check it"]]
  (fact name 1 => 2))

... will not generate three facts with three different descriptions. It will first create a fact named name. It will then process another fact declaration with an identical name. Midje considers that a redefinition (as with reloading a file of facts in the repl), and so it replaces the previous definition. The same happens with the third iteration. The end result is a single fact named name.

To get the desired effect, define :midje/description in a map:

(doseq [name ["check it" "double check it" "*triple* check it"]]
  (fact {:midje/description name} 1 => 2))

That produces this output:

FAIL "check it" at (NO_SOURCE_FILE:3)
    Expected: 2
      Actual: 1

FAIL "double check it" at (NO_SOURCE_FILE:3)
    Expected: 2
      Actual: 1

FAIL "*triple* check it" at (NO_SOURCE_FILE:3)
    Expected: 2
      Actual: 1

Fact nesting and fact groups

With the exception of the :midje/description (which is used to compose failure messages), metadata in interior facts is ignored. You cannot, for example, choose to run only one sub-fact of an outer fact.

You might want a number of tests that are all integration tests. You might not want to put :integration metadata on each one. You can avoid that in two ways. First, you could wrap all the facts in an outermost fact:

(fact :integration
  (fact ...)
  (fact ...)
  ...)

That's a good solution if you only ever want to check all the integration facts together. It's not if you want to check individual facts. In such cases, use fact-group:

(fact-group :integration
  (fact ...)
  (fact {:speed :slow} ...)
)

The fact-group metadata is merged with the individual facts' metadata.

Tabular facts

Tabular facts can take metadata. It can be placed either after the tabular token or the enclosed fact token. That is, the following two constructs mean the same thing:

(tabular "arithmetic" :foundation
  (fact 
     (+ ?a ?b) => ?c)
     ?a     ?b    ?c
     1      2     3
     2      2     4)

(tabular 
  (fact "arithmetic" :foundation
     (+ ?a ?b) => ?c)
     ?a     ?b    ?c
     1      2     3
     2      2     4)
Something went wrong with that request. Please try again.