Skip to content

Commit e1e47ca

Browse files
mpingalexanderjamesking
authored andcommitted
Allow spying on multiple protocols
1 parent d75db80 commit e1e47ca

File tree

3 files changed

+65
-34
lines changed

3 files changed

+65
-34
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,7 @@ to mock multiple protocols.
240240
```
241241

242242
You will also find a `spy` macro within the protocol namespace, this
243-
currently supports spying on an implementation for a single protocol.
244-
Caution: the protocol `spy` macro will change in the future when I add support for spying on multiple protocols, you can see examples in the
245-
`spy.protocol-test` namespace.
243+
can also be used to spy on multiple protocols: `(spy.protocol/spy Proto1...ProtoN impl)`
246244

247245
## Contributing
248246

src/clj/spy/protocol.clj

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,33 @@
4545
methods))
4646

4747
(defmacro spy
48-
"Reify the protocol, spies attached to the reified object via metadata"
48+
"Reify the protocols, spies attached to the reified object via metadata.
49+
Can accept multiple protocols at once: (spy.protocol/spy Proto1...ProtoN impl)"
4950
{:style/indent [:defn [1]]}
50-
[protocol instance]
51-
(let [methods (vals (protocol-methods @(resolve protocol)))
51+
[& args]
52+
(let [protocols (butlast args)
53+
instance (last args)
54+
proto-methods (reduce (fn [acc protocol]
55+
(assoc acc protocol (vals (protocol-methods @(resolve protocol)))))
56+
{}
57+
protocols)
58+
all-methods (->> proto-methods vals (apply concat))
5259
spy-fns-sym (gensym "spy-fns-")]
53-
`(let [~spy-fns-sym ~(->spy-fns methods instance)]
60+
61+
`(let [~spy-fns-sym ~(->spy-fns all-methods instance)]
5462
(with-meta
55-
(reify ~protocol
56-
~@(mapcat (fn [{:keys [name arglists]}]
57-
(map (fn [arglist]
58-
(let [args (->args arglist)]
59-
(list name args (concat (list (list (keyword name) spy-fns-sym)) args))))
60-
arglists))
61-
methods))
63+
~@(let [protos+impls
64+
(for [[protocol methods] proto-methods]
65+
(concat
66+
(list (symbol protocol))
67+
(mapcat (fn [{:keys [name arglists]}]
68+
(map (fn [arglist]
69+
(let [args (->args arglist)]
70+
(list name args (concat (list (list (keyword name) spy-fns-sym)) args))))
71+
arglists))
72+
methods)))]
73+
(list (concat '(reify)
74+
(apply concat protos+impls))))
6275
~spy-fns-sym))))
6376

6477
(defn spies [instance]

test/clj/spy/protocol_test.clj

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@
100100
methods (protocol-methods p)]
101101

102102
(is (= {:hello
103-
{:name 'hello
104-
:arglists [['this 'a] ['this 'a 'b 'c]]
105-
:var #'spy.protocol-test/hello}}
103+
{:name 'hello
104+
:arglists [['this 'a] ['this 'a 'b 'c]]
105+
:var #'spy.protocol-test/hello}}
106106
methods)))
107107

108108
(let [p @(resolve 'spy.protocol-test/AllSorts)
@@ -157,20 +157,40 @@
157157
(method-b [this x]))
158158

159159
(deftest multi-protocols-test
160-
(let [mock (protocol/mock
161-
ProtocolA
162-
(method-a [this x]
163-
:a)
164-
165-
ProtocolB
166-
(method-b [this x]
167-
:b))]
168-
(is (= :a (method-a mock :testing)))
169-
(is (= :b (method-b mock :test)))
170-
(is (spy/called-once-with? (:method-a (protocol/spies mock))
171-
mock
172-
:testing))
173-
174-
(is (spy/called-once-with? (:method-b (protocol/spies mock))
175-
mock
176-
:test))))
160+
(testing "mock"
161+
(let [mock (protocol/mock
162+
ProtocolA
163+
(method-a [this x]
164+
:a)
165+
166+
ProtocolB
167+
(method-b [this x]
168+
:b))]
169+
(is (= :a (method-a mock :testing)))
170+
(is (= :b (method-b mock :test)))
171+
(is (spy/called-once-with? (:method-a (protocol/spies mock))
172+
mock
173+
:testing))
174+
175+
(is (spy/called-once-with? (:method-b (protocol/spies mock))
176+
mock
177+
:test))))
178+
(testing "spy"
179+
(let [impl (reify
180+
ProtocolA
181+
(method-a [this x ] :a)
182+
ProtocolB
183+
(method-b [this x] :b))
184+
spied (protocol/spy ProtocolA ProtocolB impl)]
185+
186+
(is (= :a (method-a spied :testing)))
187+
(is (= :b (method-b spied :test)))
188+
189+
(is (spy/called-once-with? (:method-a (protocol/spies spied))
190+
spied
191+
:testing))
192+
193+
(is (spy/called-once-with? (:method-b (protocol/spies spied))
194+
spied
195+
:test)))))
196+

0 commit comments

Comments
 (0)