-
Notifications
You must be signed in to change notification settings - Fork 129
Chatty checkers
Suppose I'm writing a Clojure version of Ruby's Sequel RDBMS facade, and it's time to make INSERT work. Here's a first test:
(fact
(-> (datastate :countries) (insert {:region "North America" :name "USA"})) => anything
(datastate :countries) => (SOME-CHECKER {:region "North America", :name "USA"}))
I want the as-yet unwritten checker to check the entire :countries
"datastate" (table) against its argument, except that it should ignore unmentioned columns. (For this test, I don't care about the :id
column.)
Astoundingly enough, there's a built-in checker that's almost what I want. It's called only-maps-containing
. The name is not great. Suggestions for a better one? There's also a maps-containing
which allows both extra keys and extra maps. This one insists that there be no maps not mentioned in the expected result. There's a problem using it, though. A datastate isn't the contents of the table. Think of it as an as-yet-unexecuted query. To execute the query, I can call all
on the datastate. Like this:
(all (datastate :countries)) => (only-maps-containing {:region "North America", :name "USA"}))
That works, but I'm going to have a zillion tests that use all
, and I'd rather bury that away in a helper function. That is, in this:
(datastate :countries) => (rows-matching {:region "North America", :name "USA"}))
... where rows-matching
looks like this:
(defn rows-matching [& expected-rows]
(fn [actual-datastate]
( (only-maps-containing expected-rows) (all actual-datastate))))
That works fine when the test passes. But here's what gets printed when it fails:
FAIL at (write.clj:41)
Actual result did not agree with the checking function.
Actual result: {:table :countries}
Checking function: (rows-matching {:region "North America", :name "USA"})
FAILURE: 1 fact was not confirmed. (But 1 was.)
This isn't such a helpful message. We don't want to see dataset, we want to see (all dataset)
. Chatty checkers make this possible. Just change the fn
in the rows-matching
definition to chatty-checker
, like this:
(defn rows-matching [& expected-rows]
(chatty-checker [actual-datastate]
( (only-maps-containing expected-rows) (all actual-datastate))))
Now we get this kind of error message:
FAIL at (write.clj:41)
Actual result did not agree with the checking function.
Actual result: {:table :countries}
Checking function: (rows-matching {:region "North America", :name "USA"})
During checking, these intermediate values were seen:
(all actual-datastate) => []
-
Chatty checkers only print the results of top-level functions. That is, you'll get two lines of output from the following. The first will show the result of
(f 1)
and the second will show(f (g 1))
.*my-special-value*
and(g 23)
won't be shown. I'm hoping that strikes a decent balance between the annoyance of noisy output and the annoyance of not seeing a value you need. (And the implementation's easier this way.) Let me know.(chatty-checker [actual] (truth-valued-function *my-special-value* (f 1) (f (g 23))))