unit testing - clojure.test (is (thrown? ...) not seeing exception

Keywords:unit  testing 


I have a function param-values that throws an IllegalArgumentException when it cannot find a key in a Liberator context. I have a clojure.test unit test for this:

(testing "Non-existing key" 
  (is (thrown? IllegalArgumentException (param-values ctx [:baz]))))

For some reason, this test is failing, even though I can see that my function is behaving correctly in the REPL:

user> (param-values ctx [:baz])
IllegalArgumentException Missing required param baz  resources/param-value (resources.clj:57)

user> (is (thrown? IllegalArgumentException (param-values ctx [:baz])))

FAIL in clojure.lang.PersistentList$EmptyList@1 (form-init2687593671136401208.clj:1)
expected: (thrown? IllegalArgumentException (param-values ctx [:baz]))
  actual: nil

param-values itself is quite simple; it just maps over the specified args with param-value:

(defn param-values [ctx args & [{:keys [optional-args] :as opts}]]
  (let [params (or (get-in ctx [:request :params]) {})
        args (concat args optional-args)]
    (map #(param-value params % opts) args)))

Of course, I have more in-depth tests for param-value, one of which is:

(testing "Arg not present"
    (is (thrown-with-msg?
         IllegalArgumentException #"Missing required param baz"
         (param-value {} :baz))))

This test passes!

What gives? Is something about what I'm doing not jiving with the clojure.test/is macro? Do I have a typo that I'm too thick to see?

1 Answer: 

Is it because param-values returns a lazy sequence?

When using thrown-with-msg? it calls re-find on the return value of (param-value {} :baz) and thus evaluates the lazy sequence. This does not happen when just using thrown? instead.

So could you try this instead:

(is (thrown? IllegalArgumentException
      (doall (param-values ctx [:baz]))))

doall will evaluate the lazy sequence.

Update based on comments:

As you are not interested in the return value of the lazy sequence, but only the side effects (throwing an exception), it's preferred to use dorun instead of doall.