Skip to content

Commit 63632e2

Browse files
committed
Fixes #11 by rewriting in logic
Also fixes how test messages are combined with 'within' messages.
1 parent d922094 commit 63632e2

File tree

2 files changed

+101
-81
lines changed

2 files changed

+101
-81
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased changes on **master**:
22

3+
* Improve failure reporting for `in`; allow it to be combined with `more` etc. #11
34
* Add support for mocking return values in `side-effects`.
45
* Add support for optional message argument in `expect`. #9
56
* Added article-style documentation for cljdoc.org. #6, #7, #8, #10

src/expectations/clojure/test.clj

Lines changed: 100 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -135,87 +135,106 @@
135135
([e a] `(expect ~e ~a nil true ~e))
136136
([e a msg] `(expect ~e ~a ~msg true ~e))
137137
([e a msg ex? e']
138-
(let [msg' (str/join
139-
"\n"
140-
(cond-> []
141-
msg
142-
(conj msg)
143-
(when-not (= e e'))
144-
(conj
145-
(str " within: "
146-
(pr-str (if (and (sequential? e') (= 'expect (first e')))
147-
e'
148-
(list 'expect e' a)))))))]
149-
(cond
150-
(and (sequential? a) (= 'from-each (first a)))
151-
(let [[_ bindings & body] a]
152-
(if (= 1 (count body))
153-
`(doseq ~bindings
154-
(expect ~e ~(first body) ~msg ~ex? ~e))
155-
`(doseq ~bindings
156-
(expect ~e (do ~@body) ~msg ~ex? ~e))))
157-
158-
(and (sequential? a) (= 'in (first a)))
159-
(let [form `(~'expect ~e ~a ~msg)]
160-
`(let [a# ~(second a)]
161-
(cond (or (sequential? a#) (set? a#))
162-
(let [all-reports# (atom nil)]
163-
(with-redefs [t/do-report (all-report all-reports#)]
164-
(doseq [~'x a#]
165-
;; TODO: really want x evaluated here! (not sure about msg)
166-
(expect ~'x ~e nil ~ex? ~form)))
167-
(if (contains? @all-reports# :pass)
168-
;; report all the passes (and no failures or errors)
169-
(doseq [r# (:pass @all-reports#)] (t/do-report r#))
170-
(do
171-
;; report all the errors and all the failures
172-
(doseq [r# (:error @all-reports#)] (t/do-report r#))
173-
(doseq [r# (:fail @all-reports#)] (t/do-report r#)))))
174-
(map? a#)
175-
(let [e# ~e]
176-
(expect e# (select-keys a# (keys e#)) ~msg ~ex? ~form))
177-
:else
178-
(throw (IllegalArgumentException. "'in' requires map or sequence")))))
179-
180-
(and (sequential? e) (= 'more (first e)))
181-
(let [es (mapv (fn [e] `(expect ~e ~a ~msg ~ex? ~e')) (rest e))]
182-
`(do ~@es))
183-
184-
(and (sequential? e) (= 'more-> (first e)))
185-
(let [es (mapv (fn [[e a->]]
186-
(if (and (sequential? a->)
187-
(symbol? (first a->))
188-
(let [s (name (first a->))]
189-
(or (str/ends-with? s "->")
190-
(str/ends-with? s "->>"))))
191-
`(expect ~e (~(first a->) (? ~a) ~@(rest a->)) ~msg false ~e')
192-
`(expect ~e (-> (? ~a) ~a->) ~msg false ~e')))
193-
(partition 2 (rest e)))]
194-
`(do ~@es))
195-
196-
(and (sequential? e) (= 'more-of (first e)))
197-
(let [es (mapv (fn [[e a]] `(expect ~e ~a ~msg ~ex? ~e'))
198-
(partition 2 (rest (rest e))))]
199-
`(let [~(second e) ~a] ~@es))
200-
201-
(and ex? (symbol? e) (resolve e) (class? (resolve e)))
202-
(if msg'
203-
(if (isa? (resolve e) Throwable)
204-
`(t/is (~'thrown? ~e ~a) ~msg')
205-
`(t/is (~'instance? ~e ~a) ~msg'))
206-
(if (isa? (resolve e) Throwable)
207-
`(t/is (~'thrown? ~e ~a))
208-
`(t/is (~'instance? ~e ~a))))
209-
210-
(isa? (type e) java.util.regex.Pattern)
211-
(if msg'
212-
`(t/is (re-find ~e ~a) ~msg')
213-
`(t/is (re-find ~e ~a)))
214-
215-
:else
216-
(if msg'
217-
`(t/is (~'=? ~e ~a) ~msg')
218-
`(t/is (~'=? ~e ~a)))))))
138+
(let [within (if (and (sequential? e') (= 'expect (first e')))
139+
`(pr-str '~e')
140+
`(pr-str (list '~'expect '~e' '~a)))
141+
msg' `(str/join
142+
"\n"
143+
(cond-> []
144+
~msg
145+
(conj ~msg)
146+
~(not= e e')
147+
(conj (str " within: " ~within))))]
148+
(cond
149+
(and (sequential? a) (= 'from-each (first a)))
150+
(let [[_ bindings & body] a]
151+
(if (= 1 (count body))
152+
`(doseq ~bindings
153+
(expect ~e ~(first body) ~msg ~ex? ~e))
154+
`(doseq ~bindings
155+
(expect ~e (do ~@body) ~msg ~ex? ~e))))
156+
157+
(and (sequential? a) (= 'in (first a)))
158+
(let [form `(~'expect ~e ~a)]
159+
`(let [a# ~(second a)
160+
not-in# (str '~e " not found in " a#)
161+
msg# (if (seq ~msg') (str ~msg' "\n" not-in#) not-in#)]
162+
(cond (or (sequential? a#) (set? a#))
163+
(let [all-reports# (atom nil)
164+
one-report# (atom nil)]
165+
;; we accumulate any and all failures and errors but we
166+
;; only accumulate passes if each sequential expectation
167+
;; fully passes (i.e., no failures or errors)
168+
(with-redefs [t/do-report (all-report one-report#)]
169+
(doseq [a'# a#]
170+
(expect ~e a'# msg# ~ex? ~form)
171+
(if (or (contains? @one-report# :error)
172+
(contains? @one-report# :fail))
173+
(do
174+
(when (contains? @one-report# :fail)
175+
(swap! all-reports#
176+
update :fail into (:fail @one-report#)))
177+
(when (contains? @one-report# :error)
178+
(swap! all-reports#
179+
update :error into (:error @one-report#))))
180+
(when (contains? @one-report# :pass)
181+
(swap! all-reports#
182+
update :pass into (:pass @one-report#))))
183+
(reset! one-report# nil)))
184+
185+
(if (contains? @all-reports# :pass)
186+
;; report all the passes (and no failures or errors)
187+
(doseq [r# (:pass @all-reports#)] (t/do-report r#))
188+
(do
189+
(when-let [r# (first (:error @all-reports#))]
190+
(t/do-report r#))
191+
(when-let [r# (first (:fail @all-reports#))]
192+
(t/do-report r#)))))
193+
(map? a#)
194+
(let [e# ~e]
195+
(expect e# (select-keys a# (keys e#)) ~msg ~ex? ~form))
196+
:else
197+
(throw (IllegalArgumentException. "'in' requires map or sequence")))))
198+
199+
(and (sequential? e) (= 'more (first e)))
200+
(let [es (mapv (fn [e] `(expect ~e ~a ~msg ~ex? ~e')) (rest e))]
201+
`(do ~@es))
202+
203+
(and (sequential? e) (= 'more-> (first e)))
204+
(let [es (mapv (fn [[e a->]]
205+
(if (and (sequential? a->)
206+
(symbol? (first a->))
207+
(let [s (name (first a->))]
208+
(or (str/ends-with? s "->")
209+
(str/ends-with? s "->>"))))
210+
`(expect ~e (~(first a->) (? ~a) ~@(rest a->)) ~msg false ~e')
211+
`(expect ~e (-> (? ~a) ~a->) ~msg false ~e')))
212+
(partition 2 (rest e)))]
213+
`(do ~@es))
214+
215+
(and (sequential? e) (= 'more-of (first e)))
216+
(let [es (mapv (fn [[e a]] `(expect ~e ~a ~msg ~ex? ~e'))
217+
(partition 2 (rest (rest e))))]
218+
`(let [~(second e) ~a] ~@es))
219+
220+
(and ex? (symbol? e) (resolve e) (class? (resolve e)))
221+
(if (seq `~msg')
222+
(if (isa? (resolve e) Throwable)
223+
`(t/is (~'thrown? ~e ~a) ~msg')
224+
`(t/is (~'instance? ~e ~a) ~msg'))
225+
(if (isa? (resolve e) Throwable)
226+
`(t/is (~'thrown? ~e ~a))
227+
`(t/is (~'instance? ~e ~a))))
228+
229+
(isa? (type e) java.util.regex.Pattern)
230+
(if (seq `~msg')
231+
`(t/is (re-find ~e ~a) ~msg')
232+
`(t/is (re-find ~e ~a)))
233+
234+
:else
235+
(if (seq `~msg')
236+
`(t/is (~'=? ~e ~a) ~msg')
237+
`(t/is (~'=? ~e ~a)))))))
219238

220239
(comment
221240
(macroexpand '(expect (more-> 1 :a 2 :b 3 (-> :c :d)) {:a 1 :b 2 :c {:d 4}}))

0 commit comments

Comments
 (0)