A custom test runner for clojure.test with detailed, coloured output and summaries
This project started as a complaint (a numbered list of them) on a Slack
channel about the default test runner for clojure.test
. Most of these
relating to the fact that there's not quite enough information presented to
maximally assist in debugging ... that one often has to piece things together.
Thanks to @chris-durbin, who urged an implementation follow-up
to make things better, ltest for Clojure is now a thing.
The Clojure ltest test runner was inspired by the LFE ltest runner, whence it got its name.
The ltest library is currently being used in the NASA Earthdata CMR project as a supplementary development/testing tool, and in the CMR Client library.
The basic needs ltest aims to resolve (admittedly important for only a subset of developers) are the following:
(testing ...)
call (see
ticket #11)OK
, FAIL
, ERROR
)a.b.c.d
and a.b.e.f
would both be grouped in a.b
)The functionality provided by this library may be used in several ways:
lein
plugin (not yet created; see ticket
#10)The first two are discussed below. In both cases, the ltest library is utilized solely through its primary namespace, e.g.:
(require '[ltest.core :as ltest])
lein
Plugin ↟The simplest way to use ltest is with the lein
plugin. The latest version is
here:
Add this to your project as a lein
plugin, e.g.:
...
:plugins [[lein-ltest "x.y.z"]]
...
And then run your tests with the following:
$ lein ltest
Collections of tests may be run with the (ltest/run-tests)
function. The
following example passing just one test namespace, but any number may be
passed as additional arguments:
(ltest/run-tests ['ltest.group1.samples.sample1])
Here's is a screenshot of this call's result in the ltest dev environment (click for a larger view):
Note that this includes, in order:
A similar approach with analagous reporting is available for running single tests, but instead of a namespace, a namespace-qualified test function (as var) is passed:
(ltest/run-test #'ltest.group1.samples.sample2/multiple-pass-test)
Screenshot:
In ltest, test suites are aribitrary named groupings of tests. As with
run-tests
, any number of namespaces my be provided in the :nss
vector:
(ltest/run-suite {:name "Simple Suite"
:nss ['ltest.group1.samples.sample2]})
Screenshot:
You can also define multiple suites and run them together (useful for unit and integration tests):
(def suite-1
{:name "Arbitrary Division 1"
:nss ['nogroup
'ltest.group1.samples.sample0
'ltest.group1.samples.sample1]})
(def suite-2
{:name "Arbitrary Division 2"
:nss [:ltest.group1.samples.sample2
"ltest.group1.samples.sample3"
'ltest.group2.samples.sample4
'ltest.group2.samples.sample5
'ltest.group2.samples.sample6
'ltest.group2.samples.sample7]})
(def suites
[suite-1 suite-2])
(ltest/run-suites suites)
Screenshot:
The CMR client library has opted to use ltest to build a quick
test runner that can be executed from the command line, via a lein
alias.
This is really just a workaround until ltest has an official lein
plugin.
The steps for creating a test runner are given in the following sub-sections.
For all namespaces you want to qualify as containing unit tests, simply update the namespace for the given file, e.g.,
from this:
(ns ur.proj.tests.util
...)
to this:
(ns :unit ur.proj.tests.util
...)
Likewse for integration and system tests:
(ns :integration ur.proj.tests.server
...)
(ns :system ur.proj.tests.services
...)
runner
Namespace ↟In this particular runner, the "suite" functionality of ltest is not taken
advantage of; instead, our example runner below relies upon metadata tags in
the test namespaces we've made. Note that the ltest/run-*-tests
functions are
conveniences provided by ltest; if you should want to tag your tests in any
other arbitrary manner, creating convenience functions for your tags (e.g., to
be used by the test runner) is very easy (see the ltest.core
source for
hints).
Here is a sample runner namespace, intended to be called from the command line:
(ns ur.proj.testing.runner
(:require
[cmr.client.tests]
[ltest.core :as ltest])
(:gen-class))
(def tests-regex #"ur\.proj\.tests\..*")
(defn run-tests
[]
(ltest/run-all-tests tests-regex))
(defn print-header
[]
(println)
(println (apply str (repeat 80 "=")))
(println "Your Project Test Runner")
(println (apply str (repeat 80 "=")))
(println))
(defn -main
"This can be run from `lein` in the following ways:
* `lein run-tests`
* `lein run-tests unit`
* `lein run-tests integration`
* `lein run-tests system`"
[& args]
(print-header)
(case (keyword (first args))
:unit (ltest/run-unit-tests tests-regex)
:integration (ltest/run-integration-tests tests-regex)
:system (ltest/run-system-tests tests-regex)
(run-tests)))
lein
Alias ↟Let's add an alias to easily execute our test runner from the command
line. In your project.clj
file, add a new section (if you don't already
have it) siblimg to the :profiles
or :dependencies
sections.
...
:aliases {
...
"run-tests"
^{:doc "Use the ltest runner for verbose, colourful test output"}
["with-profile" "+test" "run" "-m" "ur.proj.testing.runner"]
...}
...
Now you can use that to run the following, optionally limiting tests to what they have been tagged in their namespace:
lein run-tests
(will run all types of tests)lein run-tests unit
lein run-tests integration
lein run-tests system
In the example above, we used the arbitrary namespace metadata of :unit
,
:integration
, and :system
for our different types of tests. Now that you
have made these annotations in the test namespaces, you can use them directly
with lein
, too (without using ltest, should you so choose). Simply add this
to your project.clj
file's :test
profile:
...
:profiles {
...
:test {
...
:test-selectors {
:default :unit
:unit :unit
:integration :integration
:system :system}
}}}
...
Now you can run the following to just test the parts of the project you want:
lein test
(will just run unit tests)lein test :unit
lein test :integration
lein test :system
Copyright © 2017, Clojure-Aided Enrichment Center
Distributed under the Apache License, Version 2.0.
Can you improve this documentation? These fine people already did:
Duncan McGreggor, Ruslan Shestopalyuk & Tero PaloheimoEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close