Deprecated setup and teardown

marick edited this page Apr 27, 2013 · 3 revisions
Clone this wiki locally

The short version

Read thing-wrapped as :checks, :facts, or :contents. Note these are all keys. 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. The against-background form can appear anywhere in a fact'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
            ; C is evaluated here
            (f) => 1
            ; C is evaluated here
            (g) => 2)
        ; B is evaluated here
           ; C is evaluated here
           (f) => 1))

The longer version

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 ....)

An against-background can also be placed within a single 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:

        (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 ....))

Wrapping blocks of code larger than a fact (even more deprecated)

In addition to :facts and :checks, you can also use :contents. It refers to the entire body of the argument to against-background. 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 ...)

That is equivalent to this:

    (sql/with-connection db 
       (fact ...)
       (fact ...)
       (fact ...))

:contents is a remnant of an earlier version of Midje that didn't have nested facts. Better than either of the previous forms is this one:

    (facts "that have something to do with a connection" 
      (sql/with-connection db 
         (fact ...)
         (fact ...)
         (fact ...)))

It's better than the first because it's clearer what happens and when. It's better than the second because the with-connection won't be executed in production mode.

You can also use :contents in a background that applies to the whole file, like this:

    (background (before :contents (...code...)))
    ;; The rest of the file. *Not* wrapped by the `background` form.

I doubt there's any good reason to use this form. If you do, note that both before and after code are executed immediately. (Clojure provides no way to know when the rest of a file is fully loaded, so there's no way for "after" to mean "after all the rest of the code in the namespace".) around is similarly nonsensical.

Access to lexical scope

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]
            (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)

Limited expansion

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.

Default prerequisite facts

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