Line numbers in helper functions

marick edited this page Dec 18, 2010 · 2 revisions
Clone this wiki locally

This section is useful only if the material in compound checkers is insufficient.

Suppose I'm working on a Clojure version of Ruby's Sequel RDBMS facade. Sequel's insert function accepts seven different argument formats, and it'll be fun to replicate them in Clojure. Here's the first:

 (demo-insertion-style "Most convenient for typing"
 			  (insert :region "North America" :name "USA"))    

With this code, I'm indulging two of my quirks. First, I'm trying to make the test a maximally readable example of use. Second, I don't want any words in the test that aren't essential to its purpose. In this particular set of tests, I'm going to insert the same values into an empty table in different ways, so the only thing that varies among the tests is the documentation and the particular call to insert. Because of the variety of styles, I'm making demo-insertion-style a macro. Here it is:

 (defmacro demo-insertion-style [_ insertion-form]
   `(do
       (fact (-> (datastate :countries) ~insertion-form) => anything) 
       (fact (datastate :countries) => (rows-matching {:region "North America", :name "USA"}))))

That works fine, but look what happens when I add a second style and run the test (expecting a failure). Here's the style, with its line numbers:

  42: (demo-insertion-style "Passing in a map is easier for programs"
  43:     (insert {:region "North America" :name "USA"}))

And here's what happens when I run it:

 FAIL at (write.clj:null)           ;<=========================
 Actual result did not agree with the checking function.
     Actual result: {"this Throwable was captured by midje:" #<IllegalArgumentException java.lang.IllegalArgumentException: No value supplied for key: {:region "North America", :name "USA"}>}
 Checking function: midje.sweet/anything
 FAILURE: 1 fact was not confirmed. (But 3 were.)

The place where the failure happened is identified as null. We'd much prefer it was either 42 or 43. Midje tries to give good line numbers, but sometimes it can't. In those cases, you have to supply them. Here's how:

 (defmacro demo-insertion-style [_ insertion-form]
   `(do
      (fact (-> (datastate :countries) ~insertion-form) => anything
        :file-position (midje.util.file-position/user-file-position))        ;<==========
      (fact (datastate :countries) => (rows-matching {:region "North America", :name "USA"})
        :file-position (midje.util.file-position/user-file-position))))      ;<==========

Passing the file position into the fact overrides its guess. (You can override anything fact does. If you want to do that, you need to look at the map produced by midje.semi-sweet.semi-sweet-internals/call-being-tested. Prerequisites (mocks, fakes) are also maps whose components can be overridden. For those, look at midje.semi-sweet/fake.)

Here's the result of running the test:

 FAIL at (write.clj:42)  ;<===============
 Actual result did not agree with the checking function.
     Actual result: {"this Throwable was captured by midje:" #<IllegalArgumentException java.lang.IllegalArgumentException: No value supplied for key: {:region "North America", :name "USA"}>}
 Checking function: midje.sweet/anything