Created: 2011-07-17 05:04
Updated: 2015-08-13 22:57



Powerful test runner for clojure

test-this allows you to run your clojure.test tests easily. Some of the features are:

  • Fine-grained selection of which namespaces and test functions to run
  • Automatically reload modified files
  • No changes to source code needed, use all your clojure.test tests with all its features (including proper handling of fixtures)
  • Extensible
  • Easy to use.



Add test-this as a dev-dependency. If you're using leiningen:

:dev-dependencies [[test-this "0.2.4"]]

If you're using maven, you already know how to do this.

Now, start a REPL and get test-this functions

(use 'test-this)

To run all your tests you can do


This will search all tests in your test directory and run them all.

Go code a bit more, modify your code and your tests. Now run (run-tests) again on the same REPL. The files you modified and the ones depending on them will be reloaded, and all tests will be run again. You don't need to do anything else, test-this will reload only what needs to be reloaded.

Selecting which test namespaces to run

Maybe you don't want to run all your test namespaces. You can do fine-grained selection of which namespaces you want to run:

(run-tests :namespaces ['my-lib.test-foo 'my-lib.test-bar])

Will run only the tests contained in my-lib.test-foo and my-lib.test-bar namespaces.

You can even run all namespaces matching a regular expression

(run-tests :namespaces [#".*foo[-\.]bar"])

Or maybe, you can use metadata to identify which namespaces to run:

(ns ^:wip test.foo)
(deftest ....)

And now, just do

(run-tests :namespaces [:wip])

And all your Work In Progress namespaces will be tested.

If you want to run all tests but those which are work in progress, you can use

(run-tests :namespaces [:!wip])

The keyword :!foo will match any namespace which doesn't have a thruthy metadata keyword :foo.

If you need total control, you can pass a predicate that will receive the namespace

(run-tests :namespaces [(fn [ns] (= (ns-name ns) "test.foo"))])

All elements of the :namespaces vector are ored together. So if you do

(run-tests :namespaces [:wip 'my-lib.test-foo])

All Work In Progress tests AND tests in my-lib.test-foo will be run.

If you need "and" logic, use a vector:

(run-tests :namespaces [[:wip :integration]])

will run only tests in namespaces with metadata keys :wip AND :integration.

Selecting which test functions to run

The same logic applies to selecting test functions

(deftest ^:wip test-my-foo
  (is true))

(run-tests :tests [:wip])

Will run only :wip tests in all namespaces. Of course, you can combine everything, use:

(run-tests :namespaces [[:integration #".*edit.*"]]
           :tests [:!wip])

to run tests in integration namespaces matching "edit", and only not work in progress tests.

Force reload

If for any reason you need to reload all namespaces, even if they are not modified, you can call (reset-changes) function. The next time you run the tests, all namespaces will be reloaded.


Further options to run-tests include:

  • :test-dir is a string with the directory where your tests reside. Default: "test".
  • :reload-dirs is one or a sequence of directories to watch for changes and reload if needed. Default: ["src" "test"].
  • :reload? if false, no reload will be done, even if source files changed. Default: true.
  • :before is a function called without arguments before executing any tests. Default: noop
  • :after is a function called with the results map after executing all tests. Default: identity.

If you need more control, take a look at the documentation.


If you want to use run-tests on your scripts, it may be useful to know if any tests failed. For this you can do

(when (failed? (run-tests))
  (System/exit 1))


  • Autotest
  • re-run failing tests
  • Add clojure.stacktrace
  • Add difftest
  • Leiningen plugin


Copyright (C) 2011 Sebastián B. Galkin

Distributed under the Eclipse Public License, the same as Clojure.

Cookies help us deliver our services. By using our services, you agree to our use of cookies Learn more