【问题标题】:Passing vars between deftests in Clojure unit tests在 Clojure 单元测试中的 deftests 之间传递变量
【发布时间】:2018-10-07 22:17:09
【问题描述】:

我正在尝试编写一些单元测试,以测试一些数据库模型。

我希望我的第一个 deftest 函数获取插入用户的 db 生成的 ID,将其保存在某个位置,以便下一个 deftest 可以从 db 中获取同一用户。

我认为我可以通过在第一个 deftest 中使用 def 来做到这一点,但定义的 var 在第二个 deftest 中被认为是 Unbound Var。这是我的代码:

(deftest test-user->db
  (testing "Adding a new user record to db"
    (->> (user->db {:name "Bob"})  ;; returns a UUID ID
         (def mock-user-id))       ;; binding it here
    (is (uuid? mock-user-id))))    ;; this passes

(deftest test-db->user
  (testing "Getting a user record from db"
    (let [user (db->user mock-user-id)] ;; mock-user-id is unbound :-/
      (is (instance? User user))
      (is (contains? user :id))
      (is (contains? user :name))
      (is (= mock-user-id (:id user)))
      (is (= "Bob" (:name user))))))

很遗憾,我找不到任何资源来说明为什么会这样。我只能假设命名空间的全局变量会以某种方式为每个 deftest 重置?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    显然测试不一定按顺序运行:

    (deftest a-test
      (println "a")
      (def inner "hello"))
    
    (deftest b-test
      (println "b")
      (println "inner" inner))
    
    Testing tic-tac-toe.core-test
    b
    inner #object[clojure.lang.Var$Unbound 0x37acbc9e Unbound: #'tic-tac-toe.core-test/inner]
    a
    
    Ran 2 tests containing 0 assertions.
    0 failures, 0 errors.
    => {:test 2, :pass 0, :fail 0, :error 0, :type :summary}
    

    请注意如何首先打印“b”。这意味着您的 var 可能尚未在需要时初始化。

    我会通过声明 mock-user-id 顶级来解决这个问题,然后在两个测试中都使用它,这样测试顺序就无关紧要了。如果您不希望在绝对需要之前对其进行初始化,可以将其包装在 delay 中:

    ; delay will delay initialization until it's first used
    (def mock-user-id (delay (user->db {:name "Bob"})))
    
    (deftest test-user->db
      (testing "Adding a new user record to db"
        (is (uuid? @mock-user-id)))) ; @ to force the delay to run its body the first time
    
    (deftest test-db->user
      (testing "Getting a user record from db"
        (let [user (db->user @mock-user-id)] ;; mock-user-id is unbound :-/
          (is (instance? User user))
          (is (contains? user :id))
          (is (contains? user :name))
          (is (= @mock-user-id (:id user)))
          (is (= "Bob" (:name user))))))
    

    【讨论】:

    • @cyehia 雅,np。请注意,delay 可能不是必需的。如果user->db 很昂贵,您可能会发现延迟调用它直到测试运行而不是文件运行时调用它会很好。
    【解决方案2】:

    我想在上面@carcigenicate 的答案中添加内容,以防其他人偶然发现这个主题并希望按顺序运行某些测试功能(因为默认情况下,它们不会按顺序运行)。

    在特定测试的 ns 中,创建一个名为 test-ns-hook 的函数,并按照您希望它们运行的​​顺序调用您的测试函数。如果在 ns 中定义了 test-ns-hook,则测试运行器将简单地运行该函数,而不是在该 ns 中定义的所有测试。

    所以在我的问题中,我在文件末尾添加了这个函数:

    (defn test-ns-hook []
      (test-user->db)
      (test-db->user))
    

    这确保test-user->db首先运行并完成,绑定变量,然后test-db->user在绑定变量的情况下运行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-03
      相关资源
      最近更新 更多