-
Notifications
You must be signed in to change notification settings - Fork 129
Setup and teardown
Testing code that works with mutable state often requires that you change that state before or after a fact or individual prediction. Midje's mechanism for doing that is too baroque, and it will be changed in the future. It's best if you use only features that will translate easily to the (likely) new mechanism. That will save you trouble when the current mechanism is deprecated. To that end, only such features are described here. If you want all the details, the old documentation is still available.
Midje can execute code before facts, after facts, and around facts.
We'll just use an atom to represent state:
(def state (atom nil))
Here is an expression that sets the atom to 0 before every fact in its scope:
(against-background [(before :facts (reset! state 0))]
(fact ...)
(fact ...)
...)
There was thought behind my choice of the word "background", but it was not good thought, so you might as well consider against-background
an arbitrary token. before
can take arguments other than :facts
, but I recommend you not use them, and they're not documented here.
Annoyingly, before
only accepts a single form. If you give more than one, you'll get an error message something like this:
Midje could not understand something you wrote:
`(before :facts (println "Before code") (reset! state inc))`.
`before` has two forms: `(before <target> <form>)` and `(before <target> <form> :after <form>).
To have multiple forms, you'll have to use do
:
(against-background [(before :facts (do (println "Before code")
(reset! state inc)))])
(fact...))
Sorry about that.
If nested facts both have before
forms, they are evaluated in outside-in order for each nested fact. Consider the following:
(facts swap!
(against-background [(before :facts (reset! state 0))]
(fact "uses a function to update the current value"
(swap! state inc)
@state => 1)
(fact "that function can take additional args"
(swap! state - 33)
@state => -33)
(fact "swap returns the new value"
(swap! state inc) => 1)))
The output is this:
outer setup
starting outer fact
going to check inner fact
outer setup ;; note that the outer setup gets done *again* for the inner fact
inner setup
here I am in the inner fact
done checking inner fact
finishing outer fact
This is likely The Wrong Thing, and the new version will probably not do it.
If you need code executed after facts, use after
:
(against-background [(after :facts (swap! state dec))]
(fact ...)
(fact ...))
Like before
, after
takes only one form to evaluate.
Nested after
expressions are executed in inside-out order, the opposite of before
. Consider the following:
(against-background [(before :facts (println "outer in"))
(after :facts (println "outer out"))]
(against-background [(before :facts (println " inner in"))
(after :facts (println " inner out"))]
(fact (+ 1 1) => 2)))
Here's what's printed:
outer in
inner in
inner out
outer out
It's not uncommon for Clojure libraries to provide wrapping macros that don't give you direct access to state. Consider this example of product code:
(with-telemetry-from (launch "sr81" ...)
(set-rocket-countdown! 10))
If you want multiple tests of the results of a particular launch configuration, you might be faced with considerable awkward duplication of code. (Each fact would have to describe the same configuration.)
also show an example of let