-
Notifications
You must be signed in to change notification settings - Fork 128
Setup, Teardown, and State
Read thing-wrapped as :checks
, :facts
, or :contents
. Note these are all keywords. They're explained below.
A wrapper is one of the following:
-
(around
thing-wrapped(... ?form ...))
Whenever the thing-wrapped is seen, it is substituted for the
?form
. Note the leading ? --- it's required. -
(before
thing-wrapped(
code) :after (
code))
The two code blocks are executed before and after the thing-wrapped. The
:after
and what follows can be omitted. The:after
code is executed even if the thing-wrapped throws an exception. -
(after
thing-wrapped(
code))
The code block is executed after the thing-wrapped, even if an exception was thrown. Nothing's done before it.
Zero or more wrappers can be found in three different forms:
-
(against-background [
wrappers] ...)
The wrappers apply to all the forms within the
against-background
. -
(fact ... (against-background
wrappers) ...)
Semantically, this is the same as an
against-background
that wraps this single fact. Theagainst-background
form can appear anywhere in afact's
top-level forms, and there can be more than one. Note that it's not surrounded by[]
. -
(background
wrappers...)
The wrappers take effect immediately and apply until they're erased by another
background
.
The following shows what different things-wrapped mean:
(against-background [ (before :contents (A)) (before :facts (B)) (before :checks (C)) ]
; A is evaluated here.
; B is evaluated here
(fact
; C is evaluated here
(f) => 1
; C is evaluated here
(g) => 2)
; B is evaluated here
(fact
; C is evaluated here
(f) => 1))
The following defines setup for each fact that follows it in the file. An atom is initialized to zero, and a database connection is made available in a local variable.
(background (before :facts (reset! my-atom 0))
(around :facts (sql/with-connection db ?form)))
The same can be done to a selected group of facts by wrapping them with against-background
:
(against-background [ (before :facts (reset! my-atom 0))
(around :facts (sql/with-connection db ?form))) ]
(fact...)
(fact ....)
An against-background
can also be placed within a single fact:
(fact
(swap! my-atom inc) => 1
(swap! my-atom inc) => 2
(against-background (before :facts (reset! my-atom 0))))
This means the same thing as an against-background
that surrounds the fact. It's convenient if you use magical keypresses to send a whole fact to a REPL to be evaluated. You can put the against-background
at the start of the fact or the end (or the middle): wherever you think is clearest.
Notice in the above that the before
code only executes once, at the beginning of the fact. The state change made by the first check is retained at the start of the second. You can also have code that executes before each of the checks:
(fact
(swap! my-atom inc) => 1
(swap! my-atom dec) => -1
(against-background (before :checks (reset! my-atom 0))))
before
takes another form:
(before :checks (reset! my-atom 0) :after (reset! my-atom 33))
The after code executes no matter if a check fails or an exception is thrown "through it".
If you only want code to execute after a :check or :fact, use this:
(after :checks (... code ....))
In addition to :facts
and :checks
, you can also use :contents
. Suppose you want a database connection to be established for several facts, not for each of them separately. That would be done like this:
(against-background [(around :contents (sql/with-connection db ?form))]
(fact ...)
(fact ...)
(fact....))
You can also use :contents
in a background
that applies to the whole file, like this:
(background (before :contents (...code...)))
This is exactly the same thing as executing the code directly, except that the execution doesn't happen if you're in production mode.
You can think of all of this as doing a kind of macroexpansion. Consider this setup:
(background (around :facts (let [x 1] ?form))
(around :checks (let [y (+ x 2)] ?form)))
(fact (+ x y) => 3)
This means the same thing as the following:
(let [x 1]
(fact
(let [y (+ x 2)]
(+ x y) => 3))
Variables visible at the point the background is defined are not visible when it's used to expand a fact. The following will not work:
(let [x 1]
(background (around :fact (reset! my-atom x))))
...
(fact @my-atom => 1)
I said above that background expansion is like macroexpansion. Only Midje-related macros are expanded (most notably, fact
). Other macros are left alone. That's so that any side-effects of their expansion don't happen until the whole body of code is executed. That's because of...
The following works:
(background (around :facts (sql/with-connection db ?form))
(before :facts (create-country-table) :after (sql/drop-table :countries)))
(deftest different-types-of-insertion
(fact "easiest for typing"
(-> (datastate :countries) (insert :region "North America" :name "USA"))
(datastate :countries) => (rows-matching {:region "North America", :name "USA"})))
(fact "maps are easier for programs"
(-> (datastate :countries) (insert { :region "North America", :name "USA"}))
(datastate :countries) => (rows-matching {:region "North America", :name "USA"})))
It works even though deftest's
macroexpansion actually just shoves its body - unexpanded - into metadata, to be expanded later.
Since both default prerequisite facts and state wrappers use background
and against-background
, they can be mixed:
(background (around :facts (sql/with-connection db ?form))
(gerund ...verb...) => 33)
If you want to play games with background prerequisites referring to variables set up by wrappers, consider the background prerequisites to be scoped with :facts