我正在尝试在使用 core.async go 宏时编写单元测试。天真地编写测试,如下所示,似乎 go block 内的代码没有被执行。
(ns app.core-test
(:require [clojure.test :refer :all]
[clojure.core.async :as async]))
(deftest test1 []
(let [chan (async/chan)]
(async/go
(is (= (async/<! chan) "Hello")))
(async/go
(async/>! chan "Hello"))))
我已经设法使以下工作,但它非常hacky。(deftest test1 []
(let [result (async/chan)
chan (async/chan)]
(async/go
(is (= (async/<! chan) "Hello"))
(async/>! result true))
(async/go
(async/>! chan "Hello"))
(async/alts!! [result (async/timeout 10000)])))
关于如何正确执行此操作的任何建议?
最佳答案
测试是同步执行的,所以如果你去异步测试运行器不会。在 Clojure 中,您需要通过 <!!
阻止测试运行器,在 ClojureScript 中,您必须返回一个异步测试对象。这是我在所有异步 CLJC 测试中使用的通用辅助函数:
(defn test-async
"Asynchronous test awaiting ch to produce a value or close."
[ch]
#?(:clj
(<!! ch)
:cljs
(async done
(take! ch (fn [_] (done))))))
您使用它的测试,兼容 CLJC 并且看起来不那么“hacky”:
(deftest test1
(let [ch (chan)]
(go (>! ch "Hello"))
(test-async
(go (is (= "Hello" (<! ch)))))))
断言测试解除阻塞是一种很好的做法,尤其是在测试驱动开发期间,您希望避免锁定测试运行程序。此外,锁定是异步编程失败的常见原因,因此对其进行测试是非常合理的。
为此,我编写了一个类似于您的超时操作的助手:
(defn test-within
"Asserts that ch does not close or produce a value within ms. Returns a
channel from which the value can be taken."
[ms ch]
(go (let [t (timeout ms)
[v ch] (alts! [ch t])]
(is (not= ch t)
(str "Test should have finished within " ms "ms."))
v)))
您可以使用它来编写测试,例如:
(deftest test1
(let [ch (chan)]
(go (>! ch "Hello"))
(test-async
(test-within 1000
(go (is (= "Hello" (<! ch)))))))
关于unit-testing - 如何对 clojure.core.async go 宏进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30766215/