-
Notifications
You must be signed in to change notification settings - Fork 128
Chatty checkers
Chatty checkers provide extra information about a failure. There are three ways to create them. Two depend on a checker function having the right "shape". The other is less convenient, but more general.
Suppose you want to check integer results to see if they're not only primes, but specifically Mersenne primes. Mersenne primes are both Marsenne numbers and primes. You could write such a checker this way:
(defn mersenne-prime [actual]
(and (= prime? actual)
(mersenne-number? actual)))
Here's a false fact
and its failure report:
user=> (fact 131071 => mersenne-prime)
FAIL at (t_bug.clj:27)
Actual result did not agree with the checking function.
Actual result: 131071
Checking function: mersenne-prime
That's not hugely helpful. Did it fail because it's not prime or because it's not Mersenne? To write a more expressive checker, use combining checkers:
user=> (def mersenne-prime (every-checker prime? mersenne-number?))
user=> (fact 131071 => mersenne-prime)
FAIL at (NO_SOURCE_FILE:2)
Actual result did not agree with the checking function.
Actual result: 131071
Checking function: mersenne-prime?
During checking, these intermediate values were seen:
mersenne-number? => false
false
Generous angel investors are funding our Big Data startup. Key to its success is the ability to crunch data so that the output, when "lateralized" produces a descending sequence of values. Such output is referred to as "nonelian". Put in concrete terms, success depends on implementing code such that facts like the following check out:
(fact (crunch :by-county :gauss 5.0) => nonelian)
Here's the definition of the nonelian
checker:
(defn nonelian [actual] (apply > (lateralize actual)))
And here's the result of a check:
user=> (fact (crunch :by-county :gauss 5.0) => nonelian)
FAIL at (NO_SOURCE_FILE:1)
Actual result did not agree with the checking function.
Actual result: [1.7 8.32 2.0 8.3 0.0 0.0 12.01]
Checking function: nonelian
false
This isn't horrible information, but it would also be nice to see the intermediate results of lateralize
. Fortunately, that's easy to do by using chatty-checker
in place of fn
:
user=> (def nonelian (chatty-checker [actual] (apply > (lateralize actual))))
user=> (fact (crunch :by-county :gauss 5.0) => nonelian)
FAIL at (NO_SOURCE_FILE:1)
Actual result did not agree with the checking function.
Actual result: [1.7 8.32 2.0 8.3 0.0 0.0 12.01]
Checking function: nonelian
During checking, these intermediate values were seen:
(lateralize actual) => [5.0 4.98 4.63 4.62 5.0 4.3]
false
chatty-checker
converts each top-level function application in its body into a form that reports its results.
It's equally easy to use chatty-checker
to make checkers that take arguments. Let's change "nonelian" so that there are variants. The one you've already seen is "Strictly down nonelian" (because it uses >
). "Strictly up nonelian" uses <
, etc. So here's a checker that's parameterized by direction:
user=> (defn nonelian [comparison]
(chatty-checker [actual] (apply comparison (lateralize actual))))
user=> (fact (crunch :by-county :gauss 5.0) => (nonelian >))
FAIL at (NO_SOURCE_FILE:1)
Actual result did not agree with the checking function.
Actual result: [1.7 8.32 2.0 8.3 0.0 0.0 12.01]
Checking function: (nonelian >)
During checking, these intermediate values were seen:
(lateralize actual) => [5.0 4.98 4.63 4.62 5.0 4.3]
false
Note: this implementation is nicely functional and all that, but it's actually awkward for the Midje implementation and prone to error. It may be replaced in later versions of Midje.
Midje checkables work with an extended notion of falsehood, called "data-laden falsehood". A data-laden falsehood is a map that can contain a :notes
key whose value is a sequence of strings. Those strings are printed alongside the failure.
If, for some strange reason, you wanted a checker that recorded the date of the failure, you could write it like this:
user=> (require '[midje.checking.core :as checking])
user=> (defn chatty-neg? [actual]
(or (neg? actual)
(checking/as-data-laden-falsehood {:notes [(str (new java.util.Date))]})))
user=> (fact 1 => chatty-neg?)
FAIL at (NO_SOURCE_FILE:2)
Actual result did not agree with the checking function.
Actual result: 1
Checking function: chatty-neg?
The checker said this about the reason:
Wed Feb 27 15:33:42 CST 2013
false