dposada
dposada

Reputation: 899

Is there a way in lein to run clojure tests from a jar file?

I would like to build a jar file containing a suite of clojure.test tests, and then use lein test to run those tests. As far as I can tell, :test-paths only supports paths to test source, not test jars.

Is there a way to accomplish this?

Upvotes: 1

Views: 486

Answers (1)

Alan Thompson
Alan Thompson

Reputation: 29958

You can do this by manually creating a test runner. Suppose you have a project demo and a testing ns tst.demo.core like so:

(ns tst.demo.core
  (:use demo.core clojure.test)
  (:require
    [tupelo.core :as t] ))
(t/refer-tupelo)

(deftest t1 (println "test t1"))
(deftest t2 (println "test t2"))

Under the src/demo directory, create a test driver demo.tst like so:

(ns demo.tst
  (:use clojure.test)
  (:require
    [tupelo.core :as t]
    [tst.demo.core :as tdc] )
  (:gen-class))
(t/refer-tupelo)

(defn pi []  (println 3.14))
(spyx (pi))
(spyx (#'pi))

(defn -main
  [& args]
  (spyx (ns-interns 'tst.demo.core))
  (let [tst-vars (vals (ns-interns 'tst.demo.core))]
    (spyx tst-vars)
    (doseq [tv tst-vars]
      (tv))))

We can run our driver which will call all of our tests in tst.demo.core:

> lein run -m demo.tst
(pi) => 3.14
((var pi)) => 3.14

(ns-interns (quote tst.demo.core)) => {t1 #'tst.demo.core/t1, t2 #'tst.demo.core/t2}

tst-vars => (#'tst.demo.core/t1 #'tst.demo.core/t2)

test t1
test t2

How It Works:

We digress a bit to discuss the Clojure var. The var is like a pointer from a symbol like pi to the function that prints 3.14. Normally we don't even realize the var is there, as with the first printout:

(pi) => 3.14

We invoked the pi function, which returned the value 3.14, as usual.

However, we can get a reference to the var for the pi function using the wither #'pi or (var pi). We then invoke the pi function using the var, not the symbol pi. We see:

((var pi)) => 3.14

We use the ns-interns to get a map from symbols to vars in the tst.demo.core namespace (note: it is critical that we have :require [tst.demo.core ...] at the top of the file in the ns form). We printout this map:

(ns-interns (quote tst.demo.core)) => {t1 #'tst.demo.core/t1, t2 #'tst.demo.core/t2}

We only need the vars themselves, so we grab them and save into tst-vars:

tst-vars => (#'tst.demo.core/t1 #'tst.demo.core/t2)

Note that each (dotest ...) form in the testing ns tst.demo.core creates a function that is invoked during lein test. We can invoke these functions ourselves, using the vars we just retrieved. This is what the doseq loop does, and we see the output of our mock test functions t1 and t2:

test t1
test t2

The final step is to designate demo.tst as the main ns in project.clj via

:main demo.tst

which should allow you to create a uberjar and run it:

> java  -jar  target/uberjar/demo-0.1.0-SNAPSHOT-standalone.jar             
(pi) => 3.14
((var pi)) => 3.14
(ns-interns (quote tst.demo.core)) => {t1 #'tst.demo.core/t1, t2 #'tst.demo.core/t2}
tst-vars => (#'tst.demo.core/t1 #'tst.demo.core/t2)
test t1
test t2

Upvotes: 1

Related Questions