ClojureScript

测试

ClojureScript 现在附带了 clojure.test 的端口,即 cljs.test。它试图保留 clojure.test 提供的大多数功能,以及针对单线程环境中异步测试的增强功能。

大多数功能是通过宏提供的,因为 cljs.test 依赖于编译器反射和静态变量来提供其大部分功能。

例如,您的测试 ns 表单可能看起来像这样

(ns my-project.tests
  (:require [cljs.test :refer-macros [deftest is testing run-tests]]))

编写测试

您可以使用 cljs.test/deftestcljs.test/is 来编写测试,与 clojure.test 相同。

例如,这是一个只有一个断言的简单测试

(deftest test-numbers
  (is (= 1 1)))

运行测试

您可以使用 cljs.test/run-tests 宏来运行测试。这可以在您的 REPL 中完成,也可以在您文件末尾完成。如果您有许多测试命名空间,惯用方法是创建一个测试运行器命名空间,该命名空间导入所有测试命名空间,然后调用 run-tests

您可能需要在调用 run-tests 之前添加 (enable-console-print!)

夹具

您可以使用 cljs.test/use-fixtures 宏声明夹具。您可以声明 :once 夹具或 :each 夹具。:once 夹具仅在命名空间内所有测试周围运行。:each 夹具在每个测试周围运行。与 clojure.test 不同,夹具被分成两个部分::before:after。这是为了使夹具即使在异步使用时也能正常工作。

(use-fixtures :once
  {:before (fn [] ...)
   :after  (fn [] ...)})

异步测试

由于客户端代码往往是高度异步的,而 JavaScript 是单线程的,因此 cljs.test 提供异步测试支持非常重要。您可以使用 cljs.test/async 宏来创建一个异步块。如果您编写了一个异步测试,您返回的最后一个值 *必须* 是异步块。

(deftest test-async
  (async done
    (http/get "http://foo.com/api.edn"
      (fn [res]
        (is (= res :awesome))
        (done)))))

done 是一个函数,您可以在准备好放弃控制并允许下一个测试运行时调用它。done 可以被命名为任何东西,但保持约定可能更有意义。所有测试代码都必须在异步块中。如果您在异步块中启动了多个异步进程,您将需要协调它们。这是一个使用 cljs.core.async 的好理由

(deftest test-async
  (let [url0 "http://foo.com/api.edn"
        url1 "http://bar.com/api.edn"
        res0 (http/get url0)
        res1 (http/get url1)]
    (async done
      (go
        (is (= (<! res0) :awesome))
        (is (= (<! res1) :cool))
        (done)))))

注意:您每个 deftest 表单中不能有多个异步测试,否则只会运行第一个测试。

不好

(deftest test-async
  (testing "the API is awesome" ; <-- only this test will run
    (let [url "http://foo.com/api.edn"
          res (http/get url)]
      (async done
        (go
          (is (= (<! res) :awesome))
          (done)))))
    (testing "the API is cool"
      (let [url "http://bar.com/api.edn"
            res (http/get url)]
        (async done
          (go
            (is (= (<! res1) :cool))
            (done))))))

(deftest test-async-awesome
  (testing "the API is awesome"
    (let [url "http://foo.com/api.edn"
          res (http/get url)]
      (async done
        (go
          (is (= (<! res) :awesome))
          (done))))))

(deftest test-async-cool
  (testing "the API is cool"
    (let [url "http://bar.com/api.edn"
          res (http/get url)]
      (async done
        (go
          (is (= (<! res1) :cool))
          (done))))))

异步夹具

通常,建立测试环境意味着您也需要异步夹具。这很容易实现

(use-fixtures :once
  {:before
   #(async done
      ...
      (done))
   :after
   #(do ...)})

在这种情况下,:before 需要在任何测试运行之前完成。:after 将在所有测试运行后立即完成,因为它不使用异步块。

检测测试完成和成功

通常,能够在所有测试成功完成(或不成功)后运行一些代码非常有用。由于测试可能会异步运行,因此 cljs.test/run-tests 不会返回有意义的值。但是,您可以通过向 cljs.test/report 多方法添加方法来添加测试报告事件监听器。

(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
  (if (cljs.test/successful? m)
    (println "Success!")
    (println "FAIL")))