-
Notifications
You must be signed in to change notification settings - Fork 129
Syntax and a little semantics
A fact statement starts with one of fact, facts, future-fact, future-facts.
It is followed by zero or more instances of <metadata>.
Facts can contain arbitrary Clojure code, including facts and tabular facts. That code can contain <prediction> and <provided> forms.
Example
(facts :integration
(let [a 3]
(fact (* a 2) => (+ a a))
(let [b (inc a)]
(fact
(g a b) => 33
(provided (f a) => 2))))
(println "Fact result " (fact (+ 1 1) => 2)))
Midje "compiles" facts into a value object that can be checked like this:
user=> (check-one-fact my-fact)
A tabular fact has this form: (tabular <metadata> * <fact> <heading> <row>+ ).
If the tabular form is not followed by metadata, it adopts the metadata from the enclosed fact. The result of both the tabular and enclosed fact having metadata is undefined.
The heading is a series of symbols beginning with ?
.
The meaning of a tabular fact with n rows is that of one outer-level fact with n nested facts. Each nested fact is generated by substituting (as with unification) a row's values for the corresponding symbols in the original fact. Consider the following:
(tabular
(fact "doc string" (+ ?a ?b) => ?c)
?a ?b ?c
1 2 3
8 9 17))
(fact "doc string"
(fact (+ 1 2) => 3)
(fact (+ 8 9) => 17))
It is not an error if a given heading symbol doesn't appear in the fact, nor if the fact contains a ?
symbol not named in the heading row. As a consequence, you can do clearly insane things like this:
(tabular "Crazy nesting"
(fact
(tabular (fact (+ ?a ?b) => ?c)
?a ?b
1 2
2 1))
?c
2)
Metadata is ( <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 (str 'foo)}
.
In addition to :midje/name
and :midje/description
, there is other automatic metadata. They're all defined here:
: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.
A prediction is <clojure form> <arrow> <clojure form> *<override>**.
Arrows can take several forms, such as =>
and =not=>
. See prediction arrows for the list.
An override is a key/value pair. Each prediction is converted into a map. An override overrides one of the default values for a key in the map. As there are no overrides that seem useful to users (as opposed to the Midje code itself), they are undefined in this documentation.
A provided form is (provided <prediction>* ).
The body of a provided form contains prediction forms (and only prediction forms). The interpretation of those forms is different, though. Rather than a prediction that some concrete calculation will produce a result matching a predefined right-hand side, a provided
form's prediction predicts that some function call matching the left-hand side will be made. (It also provides a concrete return value when that happens.)
A provided form must immediately follow a prediction form. The following, for example, is incorrect:
(fact
(do (f 1 1) => 2)
(provided
(g 1) => 2))
Within a provided form, one override is useful: :times <clojure form>. The value of the clojure form describes how often the prediction should come true. (By default, the prediction is to come true one or more times.) Here's an example:
(provided
(g 1) => 3 :times 2)
Prediction forms within provided
accept a different set of arrows. They are described separately.