测试chan处理函数时的结果不一致

时间:2017-02-26 07:32:17

标签: unit-testing clojure core.async midje

midje框架内测试的out.json函数会产生不一致的结果。大部分时间它按预期进行检查,但有时会在其初始状态("")处读取async-blocker。在检查之前,我依靠process-async函数在(require '[[test-with-files.core :refer [with-files public-dir]]) (defn async-blocker [fun & args] (let [chan-test (chan)] (go (>! chan-test (apply fun args))) (<!! chan-test))) (defn process-async [channel func] (go-loop [] (when-let [response (<! channel)] (func response) (recur)))) (with-files [["/out.json" ""]] (facts "About `process-async" (let [channel (chan) file (io/resource (str public-dir "/out.json")) write #(spit file (str % "\n") :append true)] (doseq [m ["m1" "m2"]] (>!! channel m)) (async-blocker process-async channel write) (clojure.string/split-lines (slurp file)) => (just ["m1" "m2"] :in-any-order) ) ) ) 上等待。

我的方法有什么问题?

class Account < ActiveRecord::Base
  before_save :encrypt_username, if: :username_changed?
  before_save :encrypt_password, if: :password_changed?

  def encrypt_username
    crypt = ActiveSupport::MessageEncryptor.new(ENV['KEY'])
    self.username = crypt.encrypt_and_sign(username)
  end

  def encrypt_password
    crypt = ActiveSupport::MessageEncryptor.new(ENV['KEY'])
    self.password = crypt.encrypt_and_sign(password)
  end

  def decrypted_username
    crypt = ActiveSupport::MessageEncryptor.new(ENV['KEY'])
    crypt.decrypt_and_verify(username)
  end

  def decrypted_password
    crypt = ActiveSupport::MessageEncryptor.new(ENV['KEY'])
    crypt.decrypt_and_verify(password)
  end
end

1 个答案:

答案 0 :(得分:1)

问题是process-async会立即返回&#34; [...]一个通道,当收到身体的结果时 已完成&#34; (因为go-loop只是(go (loop ...))的语法糖,而go立即返回)。

这意味着<!!中的屏蔽async-blocker几乎会立即显示一个值,go阻止来自process-asyncasync-blocker的顺序执行未定。可能是大多数情况下process-async的块首先执行,因为它首先被创建,但这在并发上下文中并不是很好的保证。

根据<!!的文档&#34;如果关闭则返回nil。如果没有可用的话会阻止。&#34; 这意味着如果你可以假设(apply fun args)的返回值是go返回的频道,你应该可以阻止以下列方式使用<!!

(defn async-blocker [fun & args]
  (<!! (apply fun args)))

一旦通道中有值(即go块的返回值),这将取消阻塞。

还有其他选项可以等待另一个go块的结果。例如,您可以在chan-test中提供原始fun作为参数,然后在put中创建chan-test块时go中的值fun终止。但我认为,鉴于您展示的代码,其他方法可能会更加复杂。