ClojureScript

テスト

ClojureScriptは、cljs.testという形でclojure.testの移植版を同梱するようになりました。シングルスレッド環境での非同期テストの拡張機能とともに、clojure.testで提供される機能のほとんどを維持しようとしています。

cljs.testは、その機能のほとんどを提供するためにコンパイラの reflection と静的変数に依存しているため、機能のほとんどはマクロを介して提供されます。

たとえば、テスト用の nsフォームは、おそらく次のようになります。

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

テストの記述

clojure.testと同じように、cljs.test/deftestcljs.test/isを使ってテストを書くことができます。

たとえば、1つのアサーションを持つ簡単なテストを次に示します。

(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の2つの部分に分割されます。これは、非同期で使用された場合でもフィクスチャが正しく機能するようにするためです。

(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")))