Skip to content

Folded prerequisites

marick edited this page Mar 23, 2011 · 6 revisions

I've found myself often writing prerequisites like these:

    (fact 
        (no-exclusions) => { "procedure" []}
        (provided
             (procedures) => [ ...procedure... ]
             (procedure-names [...procedure...]) => ["procedure"])))

That's less clear than I like, so Midje can also do this:

      (provided
         (procedure-names (procedures)) => ["procedure"])

That "folds" together the two earlier prerequisites into one. When a fact is checked, the folded prerequisite becomes two prerequisites that are individually checked. If no-exclusions doesn't use procedures, you'll get a message about that. If it doesn't apply procedure-names to the result of (procedures), you'll get a message about that. The message will say that procedure-names was never given the value of the metaconstant ...procedures-value-1.... Both messages would refer to the line number of the folded procedure.

Before version 1.1-preview-3, the inner function had to be the first argument to its caller. With that version:

  • the inner function can be any argument.
       (provided (procedure-names "core" (procedures 9)) => ...))
  • there can be more than one inner function.
       (provided (procedure-names (procedures) (country-code)) => ...))
  • inner functions can be nested within inner functions.
       (provided (procedure-names (procedures (veterinary (core 1)))) => ...))
  • inner functions can be repeated.
       (provided 
          (procedure-names (procedures)) => ...
          (pricing (procedures)) => ...)

Don't duplicate manually what's done automatically

Since Midje creates a prerequisite for each inner function, something like the following doesn't make sense:

(fact
 (oopsie 1) => 2
 (provided
   (h 1) => 8        ; Make (h 1) produce 8.
   (f (h 1)) => 2))  ; Make (h 1) produce something like ...h-value-1...

One of the two prerequisites won't be used, so the fact will fail.

A note on checkers

Checkers can be used as both expected values of assertions and also in the argument lists of prerequisites:

    (fact
      (f 1) => truthy   ; as expected value of assertion
      (provided 
         (g truthy) => 3))   ; as argument of prerequisite

Some checkers, like roughly, are actually functions that generate checking functions:

    (fact
      (f 1) => (exactly odd?)
      (provided
         (g (roughly 3.0)) => 800.0))

How does Midje know that (g (roughly ...)) is a checker, not a prerequisite to be folded? It is defined specially:

     (defchecker roughly
       "With two arguments, accepts a value within delta of the
        expected value. With one argument, the delta is 1/1000th
        of the expected value."
       ([expected delta]
         (checker [actual]  ; <<= Checker tags the generated function as a checker.
           (and (>= expected (- actual delta))
                (<= expected (+ actual delta)))))
       ([expected]
         (roughly expected (* 0.001 expected))))

If you define your own function-generating checkers, they should use similar macros. See Checkers within prerequisites.

Clone this wiki locally