在doseq中使用with-redefs

时间:2014-05-21 08:39:44

标签: clojure mocking

我有一个Ring处理程序,它使用几个函数来构建响应。如果这些函数中的任何一个抛出异常,则应该捕获它,以便在返回500之前可以编写自定义响应主体。

我正在为处理程序编写单元测试,并且我想确保如上所述处理由这些函数抛出的异常。我的直觉是在with-redefs

中使用doseq
(doseq [f [ns1/fn1 ns1/fn2 ns2/fn1]]
  (with-redefs [f (fn [& args] (throw (RuntimeException. "fail!"))]
    (let [resp (app (request :get "/foo")))))]
      (is (= (:status resp) 500))
      (is (= (:body resp) "Something went wrong")))))

当然,鉴于with-redefs想要更改变量的根绑定,它会将f视为符号。有没有办法让它重新绑定f引用的var?我怀疑我需要一个宏来完成这个,但我希望有人能想出一个聪明的解决方案。

2 个答案:

答案 0 :(得分:2)

with-redefs对于alter-var-root的重复调用只是糖,所以你可以自己编写这些荒谬的表格,例如:

(doseq [v [#'ns1/fn1 #'ns1/fn2 #'ns2/fn1]]
  (let [old @v]
    (try
      (alter-var-root v (constantly (fn [& args]
                                      (throw (RuntimeException. "fail!")))))
      (let [resp (app (request :get "/foo")))
        (is (= (:status resp) 500))
        (is (= (:body resp) "Something went wrong")))
      (finally (alter-var-root v (constantly old))))))

答案 1 :(得分:0)

amalloy's great answer开始,这是我在测试中使用的实用函数:

(defn with-mocks
  "Iterates through a list of functions to-mock, rebinding each to mock-fn, and calls
  test-fn with the optional test-args.

  Example:

      (defn foobar [a b]
        (try (+ (foo a) (bar b))
             (catch Exception _ 1)))

      (deftest test-foobar
        (testing \"Exceptions are handled\"

          (with-mocks
            [#'foo #'bar]
            (fn [& _] (throw (RuntimeException. \"\")))
            (fn [a b] (is (= 1 (foobar a b)))) 1 2)))"
  [to-mock mock-fn test-fn & test-args]
  (doseq [f to-mock]
    (let [real-fn @f]
      (try
        (alter-var-root f (constantly mock-fn))
        (apply test-fn test-args)
        (finally
          (alter-var-root f (constantly real-fn)))))))