Skip to content

Commit e3e4ce3

Browse files
committed
initial commit
0 parents  commit e3e4ce3

File tree

9 files changed

+265
-0
lines changed

9 files changed

+265
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.cpcache
2+
.rebl
3+
.rebl.sh

deps.edn

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{:paths ["src"]
2+
:deps {org.clojure/tools.namespace {:mvn/version "0.3.0-alpha4"}
3+
org.clojure/tools.cli {:mvn/version "0.3.5"}
4+
org.clojure/clojure {:mvn/version "1.9.0"}}
5+
:aliases {:dev {:extra-paths ["test" "dev"]
6+
:extra-deps {org.clojure/test.check {:mvn/version "0.9.0"}}}}
7+
:mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"}
8+
"clojars" {:url "https://clojars.org/repo"}}}

readme.md

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# test-runner
2+
3+
`test-runner` is a small library for running tests written with
4+
`clojure.test` and/or `clojure.spec.test` in plain Clojure projects
5+
(i.e, those that use only Clojure's built-in deps tooling, not
6+
Leiningen/boot/etc.)
7+
8+
## Rationale
9+
10+
Clojure's 1.9 release includes standalone tools for dependency
11+
resolution, classpath construction, and launching processes. Clojure
12+
also ships with a straightforward testing library, `clojure.test`.
13+
14+
Using these tools, however, there is currently no standard way to
15+
discover and run unit tests. Including a heavyweight project tool such
16+
as Leiningen or Boot just for the purpose of testing is
17+
overkill. Projects can build their own ad-hoc test runners, but these
18+
tend to lack features and tend towards the "quick and dirty," besides
19+
being nonstandard from project to project.
20+
21+
This library aims to fill in the gap and provide a standardized,
22+
easy-to-use entry entry point for running unit and property-based
23+
tests while remaining a lightweight entry in Clojure's suite of
24+
decomplected project management tools.
25+
26+
## Usage
27+
28+
Include a dependency on this project in your `deps.edn`. You will
29+
probably wish to put it in a `dev` or `test` alias. For example:
30+
31+
32+
```clojure
33+
:aliases {:dev {:extra-paths ["test"]
34+
:extra-deps {net.vanderhart/test-runner {:git/url "[email protected]:levand/test-runner"
35+
:sha "..."}}}}
36+
```
37+
38+
Then, invoke Clojure via the command line, passing
39+
`net.vanderhart.test-runner` as the main namespace to run using the
40+
`-m` option.
41+
42+
```bash
43+
clj -Cdev -Rdev -m net.vanderhart.test-runner
44+
45+
You may also supply any of the additional command line options:
46+
47+
```
48+
-d, --dir DIRNAME Name of the directory containing tests. Defaults to "test".
49+
-n, --namespace SYMBOL Symbol indicating a specific namespace to test.
50+
-v, --var SYMBOL Symbol indicating the fully qualified name of a specific test.
51+
-i, --include KEYWORD Run only tests that have this metadata keyword.
52+
-e, --exclude KEYWORD Exclude tests with this metadata keyword.
53+
-h, --help Display this help message
54+
```
55+
56+
All options may be repeated multiple times, for a logical OR effect. For example, the following
57+
invocation will run all tests in the `foo.bar` and `foo.baz` namespaces:
58+
59+
```
60+
clj -Cdev -Rdev -m net.vanderhart.test-runner -n foo.bar -n foo.baz
61+
```
62+
63+
### Using Inclusions and Exclusions
64+
65+
You can use inclusions and exclusions to run only a subset of your tests, identified by metadata on the test var.
66+
67+
For example, you could tag your integration tests like so:
68+
69+
```clojure
70+
(deftest ^:integration test-live-system
71+
(is (= 200 (http/get "http://example.com"))))
72+
```
73+
74+
Then to run only integration tests, you could do:
75+
76+
```
77+
clj -Cdev -Rdev -m net.vanderhart.test-runner -i integration
78+
```
79+
80+
Or to run all tests *except* for integration tests:
81+
82+
```
83+
clj -Cdev -Rdev -m net.vanderhart.test-runner -e integration
84+
```
85+
86+
If both inclusions and exclusions are present, exclusions take priority over inclusions.

rebl.sh

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/usr/local/bin/clojure -R:dev -C:dev -m cognitect.rebl

repl.sh

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/usr/local/bin/clojure -R:dev -C:dev

src/net/vanderhart/test_runner.clj

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
(ns net.vanderhart.test-runner
2+
(:require [clojure.tools.namespace.find :as find]
3+
[clojure.spec.alpha :as s]
4+
[clojure.java.io :as io]
5+
[clojure.test :as test]
6+
[clojure.tools.cli :refer [parse-opts]]
7+
[clojure.string :as str])
8+
(:refer-clojure :exclude [test]))
9+
10+
(defn- ns-filter
11+
[{:keys [namespace]}]
12+
(if namespace
13+
#(namespace %)
14+
(constantly true)))
15+
16+
(defn- var-filter
17+
[{:keys [var include exclude]}]
18+
(let [test-specific (if var
19+
(set (map #(or (resolve %)
20+
(throw (ex-info (str "Could not resolve var: " %)
21+
{:symbol %})))))
22+
(constantly true))
23+
test-inclusion (if include
24+
#((apply some-fn include) (meta %))
25+
(constantly true))
26+
test-exclusion (if exclude
27+
#((complement (apply some-fn exclude)) (meta %))
28+
(constantly true))]
29+
#(and (test-specific %)
30+
(test-inclusion %)
31+
(test-exclusion %))))
32+
33+
(defn- filter-vars!
34+
[nses filter-fn]
35+
(doseq [ns nses]
36+
(doseq [[name var] (ns-publics ns)]
37+
(when (:test (meta var))
38+
(when (not (filter-fn var))
39+
(alter-meta! var #(-> %
40+
(assoc ::test (:test %))
41+
(dissoc :test))))))))
42+
43+
(defn- restore-vars!
44+
[nses]
45+
(doseq [ns nses]
46+
(doseq [[name var] (ns-publics ns)]
47+
(when (::test (meta var))
48+
(alter-meta! var #(-> %
49+
(assoc :test (::test %))
50+
(dissoc ::test)))))))
51+
(defn test
52+
[options]
53+
(let [dirs (or (:dir options)
54+
#{"test"})
55+
nses (->> dirs
56+
(map io/file)
57+
(mapcat find/find-namespaces-in-dir))
58+
nses (filter (ns-filter options) nses)]
59+
(println (format "\nRunning tests in %s" dirs))
60+
(dorun (map require nses))
61+
(try
62+
(filter-vars! nses (var-filter options))
63+
(apply test/run-tests nses)
64+
(finally
65+
(restore-vars! nses)))))
66+
67+
(defn- parse-kw
68+
[s]
69+
(if (str/starts-with? s ":") (read-string s) (keyword s)))
70+
71+
72+
(defn- accumulate [m k v]
73+
(update m k (fnil conj #{}) v))
74+
75+
(def cli-options
76+
[["-d" "--dir DIRNAME" "Name of the directory containing tests. Defaults to \"test\"."
77+
:parse-fn str
78+
:assoc-fn accumulate]
79+
["-n" "--namespace SYMBOL" "Symbol indicating a specific namespace to test."
80+
:parse-fn symbol
81+
:assoc-fn accumulate]
82+
["-v" "--var SYMBOL" "Symbol indicating the fully qualified name of a specific test."
83+
:parse-fn symbol
84+
:assoc-fn accumulate]
85+
["-i" "--include KEYWORD" "Run only tests that have this metadata keyword."
86+
:parse-fn parse-kw
87+
:assoc-fn accumulate]
88+
["-e" "--exclude KEYWORD" "Exclude tests with this metadata keyword."
89+
:parse-fn parse-kw
90+
:assoc-fn accumulate]
91+
["-h" "--help" "Display this help message"]])
92+
93+
(defn- help
94+
[args]
95+
(println "\nUSAGE:\n")
96+
(println "clj -m" (namespace `help) "<test-dir> <options>\n")
97+
(println (:summary args))
98+
(println "\nAll options may be repeated multiple times for a logical OR effect.")
99+
)
100+
101+
(defn -main
102+
"Entry point for the test runner"
103+
[& args]
104+
(let [args (parse-opts args cli-options)]
105+
(if (-> args :options :help)
106+
(help args)
107+
(test (:options args)))))
108+
109+
110+
(comment
111+
112+
(-main "test" "-n" "net.vanderhart.test-runner.samples")
113+
(-main "test")
114+
115+
116+
(-main "test" "-v" "net.vanderhart.test-runner.sample-property/first-element-is-min-after-sorting"
117+
#_"-v" #_"net.vanderhart.test-runner.samples/foobar")
118+
119+
(-main "test" "-i" "integration")
120+
121+
(-main "test" "-e" "integration")
122+
123+
(-main "test")
124+
125+
(-main "-h")
126+
127+
(->> (find/find-namespaces-in-dir (io/file "test"))
128+
(map #(do (require %) %))
129+
(map ns-publics)
130+
(map vals)
131+
(apply concat)
132+
(test/test-vars)
133+
134+
)
135+
136+
137+
138+
139+
140+
)

test.sh

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/usr/local/bin/clojure -Srepro -R:dev -C:dev -m net.vanderhart.test-runner test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(ns net.vanderhart.test-runner.sample-property
2+
(:require [clojure.test.check :as tc]
3+
[clojure.test.check.generators :as gen]
4+
[clojure.test.check.properties :as prop]
5+
[clojure.test.check.clojure-test :refer [defspec]]))
6+
7+
(defspec first-element-is-min-after-sorting 100
8+
(prop/for-all [v (gen/not-empty (gen/vector gen/int))]
9+
(= (apply min v)
10+
(first (sort v)))))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(ns net.vanderhart.test-runner.samples
2+
(:require [clojure.test :as t :refer [deftest is testing]]))
3+
4+
(deftest math-works
5+
(testing "basic addition and subtraction"
6+
(is (= 42 (+ 40 2)))
7+
(is (= 42 (- 44 2))))
8+
)
9+
10+
(deftest ^:integration test-i
11+
(is (= 1 1)))
12+
13+
14+
15+

0 commit comments

Comments
 (0)