在 Clojure 的单元测试中,我们通常使用 clojure.test 的 is 来验证函数返回值。然而,当函数中存在 副作用(如 println 输出)时,如何在测试中断言它的输出呢?
本文将介绍两种方法:
with-out-str)println 捕获参数(with-redefs)with-out-str 捕获标准输出(推荐)with-out-str 会捕获代码块中所有打印到标准输出的内容,并返回字符串。
示例:
(deftest atom-watcher
(testing "use watcher"
(let [a (atom 0)
out (with-out-str ;; 捕获 println 输出
(add-watch a :print
#(println "Changed from" %3 "to" %4))
(swap! a + 2))] ;; 触发 watcher
;; 验证 atom 的值
(is (= 2 @a))
;; 验证输出内容是否包含预期字符串
(is (re-find #"Changed from 0 to 2" out)))))
(with-out-str ...)
捕获所有标准输出,并将其作为字符串返回到 out。
(add-watch a :print #(println "Changed from" %3 "to" %4))
添加 watcher,当 atom 值变化时触发,打印变化前后的值。
(swap! a + 2)
修改 atom 值,从 0 → 2,触发 watcher。
(is (re-find ... out))
使用正则匹配输出字符串,验证输出内容是否符合预期。
注意:输出可能包含换行符,用 re-find 或 clojure.string/includes? 更稳妥。
with-redefs临时替换println(精确控制)如果你想完全不产生真实输出,而是直接捕获传给println的参数,可以使用 with-redefs。
示例:
(deftest atom-watcher
(testing "use watcher with redefs"
(let [a (atom 0)
called (atom nil)] ;; 用于保存 println 调用参数
(with-redefs [println (fn [& args] (reset! called args))] ;; 临时替换 println
(add-watch a :print #(println "Changed from" %3 "to" %4))
(swap! a + 2)) ;; 触发 watcher
;; 断言捕获到的参数是否正确
(is (= ["Changed from" 0 "to" 2] @called))
;; 断言 atom 的值
(is (= 2 @a)))))