简体   繁体   中英

How can I use with-redefs to mock multiple calls to the same function?

I would like to be able to mock MyFunction however I need the mock to return different values when MyFunction is called.

Is it possible to use with-redefs to return different values based on the call order of a function?

(testing "POST /foo/bar and return ok"
  (with-redefs [->Baz (fn [_]
                    (reify MyProtocol (MyFunction [_] [{:something 1}]))
                    (reify MyProtocol (MyFunction [_] [{:something 2}])))]

    (let [response (routes/foo {:request-method :post
                            :uri            "/foo/bar"
                            :query-params   {}
                            })]

      (is (= (:status response) 200)))))

You could use a mutable collection of the return values, then return/remove values from it on each call.

(defn foo [x] (inc x)) ;; example fn to be mocked

If you wanted to mock three calls to foo returning 1, 2, and 3 respectively:

(with-redefs [foo (let [results (atom [1 2 3])]
                    (fn [_] (ffirst (swap-vals! results rest))))]
  (prn (foo 0))
  (prn (foo 0))
  (prn (foo 0))
  ;; additional calls would return nil
  (prn (foo 0)))
;; 1
;; 2
;; 3
;; nil

That uses swap-vals! to get the old/new values of the atom, but requires Clojure 1.9 or greater.

If you don't have swap-vals! you could do it (less atomically) like this:

(with-redefs [foo (let [results (atom [1 2 3])]
                    (fn [_]
                      (let [result (first @results)]
                        (swap! results rest)
                        result)))]
  ...)

We use Picomock for this, and to assert on the parameters for each call, and to assert on the number of calls. Recommended!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM