Skip to content

Commit

Permalink
issue-2: support mocking private functions
Browse files Browse the repository at this point in the history
  • Loading branch information
zhming0 committed Jan 8, 2019
1 parent ea0ef7b commit 6d5d3ed
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 20 deletions.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Temporarily redefines `var` while executing `body`.
If the right-hand side of a spec is a value, then it will create a function constantly returning the value (stub).
If the right-hand side of a spec is a function, then the var-symbol will temporarily be replaced by the function.

### Example
### Features & Example

#### Track mock's invocation history

```clojure

Expand All @@ -43,6 +45,22 @@ If the right-hand side of a spec is a function, then the var-symbol will tempora

```

#### Mock private functions with ease

```clojure
(ns ns-a)
(defn- g [] true)
(defn f [a] (g))

(ns ns-b)
(deftest mock-private-test
(with-mock [#'ns-a/g "baz"]
(is (= (f "foo") "baz"))))

```

(Note: generally, it's not a good idea to mock private functions)

## License

Copyright © 2017 Ming
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject mock-clj "0.1.0"
(defproject mock-clj "0.2.0"
:description "A minimalist & non-invasive API for mocking in Clojure"
:url "https://github.com/zhming0/mock-clj"
:license {:name "Eclipse Public License"
Expand Down
53 changes: 36 additions & 17 deletions src/mock_clj/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,49 @@
Internal functions - ignore these
"""

; Check if input is like '(var x) or #'x
(defn- var-symbol? [v]
(and
(seq? v)
(= 2 (count v))
(= (first v) 'var)))

(defn- try-strip-var [v]
(if (var-symbol? v)
(second v)
v))

(defmacro make-mock
([m] `(make-mock ~m nil))
([m stub]
`(with-meta
(fn [& ~'args]
(swap! (-> ~m meta :args) conj ~'args)
; If stub is a function, execute it
(if (fn? ~stub)
(apply ~stub ~'args)
~stub))
{:args (atom [])})))

(defn gen-redefs [[m stub & spec]]
(into
[m `(make-mock ~m ~stub)]
(when spec
(gen-redefs spec))))
([] `(make-mock nil))
([stub]
`(let [~'state (atom [])]
(with-meta
(fn [& ~'args]
(swap! ~'state conj ~'args)
; If stub is a function, execute it
(if (fn? ~stub)
(apply ~stub ~'args)
~stub))
{:args ~'state}))))

(defn- gen-redefs [[m stub & spec]]
(let [sm (try-strip-var m)]
(into
[sm `(make-mock ~stub)]
(when spec
(gen-redefs spec)))))

(defn- if-var->obj [m]
(if (var? m)
(deref m)
m))

"""
------------------------------------
APIs
"""

(defn calls [m] @(-> m meta :args))
(defn calls [m] @(-> m if-var->obj meta :args))

(defn last-call [m] (last (calls m)))

Expand Down
9 changes: 8 additions & 1 deletion test/mock_clj/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns mock-clj.core-test
(:require [clojure.test :refer :all]
[mock-clj.core :refer :all]))
[mock-clj.core :refer :all]
[mock-clj.sample-private-ns]))

(defn foo [a & rst]
(str "foo" a))
Expand Down Expand Up @@ -66,3 +67,9 @@
(is (called? foo))
(reset-calls! foo)
(is (not (called? foo)))))

(deftest private-fn-test
(with-mock [mock-clj.sample-private-ns/private-fn (constantly "ok")]
(= "ok" (#'mock-clj.sample-private-ns/private-fn 2))
(is (called? #'mock-clj.sample-private-ns/private-fn))
(is (= (last-call #'mock-clj.sample-private-ns/private-fn) [2]))))
3 changes: 3 additions & 0 deletions test/mock_clj/sample_private_ns.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(ns mock-clj.sample-private-ns)

(defn- private-fn [a] "bar")

0 comments on commit 6d5d3ed

Please sign in to comment.