(ns my-project.tests
(:require [cljs.test :refer-macros [deftest is testing run-tests]]))
ClojureScript now ships with a port of clojure.test
in the form of
cljs.test
. It attempts to preserve most of the functionality provided
by clojure.test
along with enhancements for asynchronous testing in a
single threaded environment.
Most of the functionality is provided via macros as cljs.test
relies
on compiler reflection and static vars to provide most of its
functionality.
For example your testing ns form will probably look something like the following:
(ns my-project.tests
(:require [cljs.test :refer-macros [deftest is testing run-tests]]))
You can write tests with cljs.test/deftest
and cljs.test/is
same as
with clojure.test
.
For example here is a trivial test with one assertion:
(deftest test-numbers
(is (= 1 1)))
You can run tests by using the cljs.test/run-tests
macro. This may be
done in your REPL or at the end of your file. If you have many test
namespaces it’s idiomatic to create a test runner namespace which
imports all of your test namespaces and then invokes run-tests
.
You may have to add (enable-console-print!)
before calling run-tests
You can declare fixtures with the cljs.test/use-fixtures
macro. You
can declare either :once
fixtures or :each
fixtures. :once
fixtures are run only around all tests within a namespace. :each
fixtures are run around each test. Unlike clojure.test
fixtures are
split into two parts :before
and :after
. This is so that fixtures
will work correctly even when used asynchronously.
(use-fixtures :once
{:before (fn [] ...)
:after (fn [] ...)})
As client-side code tends to be highly asynchronous and JavaScript is
single-threaded, it’s important that cljs.test
provide asynchronous
testing support. You can use the cljs.test/async
macro to create an
asynchronous block. If you write an asynchronous test the last value you
return must be the async block.
(deftest test-async
(async done
(http/get "http://foo.com/api.edn"
(fn [res]
(is (= res :awesome))
(done)))))
done
is a function that you may invoke when you are ready to
relinquish control and allow the next test to run. done
can be called
anything, but it probably makes sense to keep to the convention. All of
your testing code must be in the async block. If you launch multiple
asychronous processes in your async block you will need to coordinate
them. This is a good reason to use 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)))))
NOTE: You cannot have more than one async test per deftest
form or
only the first one will run.
Bad:
(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))))))
Good:
(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))))))
Often establishing your testing environment means you need your fixtures to be asynchronous too. This is easily accomplished:
(use-fixtures :once
{:before
#(async done
...
(done))
:after
#(do ...)})
In this case :before
will need to complete before any test can run.
:after
will complete immediately after all tests have run since it
does not use an async block.
Often it’s useful to be able to run some code after all tests have
completed successfully (or unsuccessfully). Because tests may run async
cljs.test/run-tests
does not return a meaningful value. You can
however add a test report event listener by adding a method to the
cljs.test/report
multimethod.
(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
(if (cljs.test/successful? m)
(println "Success!")
(println "FAIL")))