在Clojure中,有没有适用于测试多线程应用程序的好库或策略?

3

我想知道是否有人提出了测试多线程应用程序的良好策略。

我经常使用Midje进行测试,它非常适合测试函数...但是我不太确定如何在不让代码看起来很糟糕的情况下测试多线程代码:

  (fact "the state is modified by a thread call"
    (Thread/sleep 100)
    (check-state-eq *state* nil)
    (Thread/sleep 100)
    (modify-state-thread-call *state* :updated-value)
    (Thread/sleep 100)
    (check-state-eq *state* :updated-value))

有时由于编译时间太长,我的测试会失败,因为状态没有及时更新,所以我必须要等待更长的时间。理想情况下,我希望有一种方法来编写像这样的内容:
  (fact "the state is modified by a thread call"
    (modify-state-thread-call *state* :updated-value) 
     =leads-to=> (check-state-eq *state* :updated-value))

并且远离睡眠。有什么策略可以做到这一点吗?
2个回答

5
如果在这个示例中*state*是Clojure引用类型之一,你可以使用add-watch添加一个函数,该函数会被通知每个对该对象的更改:http://clojuredocs.org/clojure_core/clojure.core/add-watch 我建议采用一种方法,使用watch来在条件满足时传递一个promise。
(let [check-promise (promise)]
  (add-watch *state* :check-for-updated-value
    (fn [rkey refr _oldval newval]
       (when (some-check newval)
          (remove-watch refr rkey)
          (deliver check-promise true))))
  (modify-state-thread-call *state* :updated-value)
  (deref check-promise 1000 false))

如果*state*在1000ms内取得满足some-check的值,此函数将立即返回true。如果条件未能达成,在1000ms后将返回false。


我遇到的问题是需要编写代码来等待一定长度的时间。有时由于编译的原因,时间会有很大的变化。虽然我对 Promise 的概念不太熟悉,但我喜欢这个想法。 - zcaudate
哦...再看一遍代码后...我现在明白发生了什么。这很酷。 - zcaudate
是的,它会短路并在条件满足时立即完成。您可以轻松地将其封装起来,以便编写(wait-for some-ref some-predicate timeout)如果您写了一个错误,可以将超时留空,这样测试就会永远挂起 ;) - Crate

1
根据Crate的回复,我创建了一个wait函数:
(defn return-val [p ms ret]
  (cond (nil? ms) (deref p)
        :else (deref p ms ret)))

(defn wait
  ([f rf] (wait f rf nil nil))
  ([f rf ms] (wait f rf ms nil))
  ([f rf ms ret]
     (let [p (promise)
           pk (hash-keyword p)
           d-fn (fn [_ rf _ _]
                  (remove-watch rf pk)
                  (deliver p rf))]
       (add-watch rf pk d-fn)
       (f rf)
       (return-val p ms ret))))

它的用法是:

(defn threaded-inc [rf]
  (future
    (Thread/sleep 100)
    (dosync (alter rf inc)))
  rf)

(def arf (ref 0))
(deref (threaded-inc arf)) ;=> 0

(dosync (ref-set arf 0))
(deref (wait threaded-inc arf)) ;=> 1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接