Last active
July 3, 2023 21:08
-
-
Save joelittlejohn/2ecc1256e5d184d78f30fd6c4641099e to your computer and use it in GitHub Desktop.
Dynamically generate clojure.test deftests (and other tricks)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns dynamic.test | |
(:require [clojure.test :refer :all])) | |
;; This example shows how tests can be generated dynamically, by | |
;; creating new vars with the correct metadata. | |
(defn add-test | |
"Add a test to the given namespace. The body of the test is given as | |
the thunk test-fn. Useful for adding dynamically generated deftests." | |
[name ns test-fn & [metadata]] | |
(intern ns (with-meta (symbol name) (merge metadata {:test #(test-fn)})) (fn []))) | |
(dotimes [i 10] | |
(add-test (str "two-times-" i "-is-even") | |
'dynamic.test | |
#(is (even? (* 2 i))))) | |
;; clojure.test will use test-ns-hook if it is defined, allowing the | |
;; order of tests to be explicit. This implementation re-uses the | |
;; clojure.test/test-vars function, but sorts the vars first. | |
(defn test-ns-hook | |
"Run tests in a sorted order." | |
[] | |
(test-vars (->> (ns-interns 'dynamic.test) vals (sort-by str)))) | |
;; The test runner can be customized by defining your own | |
;; versions of multimethods like clojure.test/report. This one prints | |
;; the test name before running (with some ASCII escapes to turn the | |
;; text green) | |
(defmethod clojure.test/report :begin-test-var | |
[m] | |
(println "\u001B[32mTesting" (-> m :var meta :name) "\u001B[0m")) | |
;; clojure.test allows 'fixtures' to be added to wrap behaviour around | |
;; tests (or suites of tests). Here we add a simple fixture to output | |
;; the time each test takes: | |
(defn with-timing | |
"Fixture fn to print the time taken to test (in ms) after a test | |
completes." | |
[test] | |
(time (test))) | |
;; then in your test namespace: | |
(use-fixtures :each with-timing) | |
;; Sometimes you want to customize test reporting, but keep the original | |
;; behaviour too. Here's a simple way to do that: | |
(let [original-fn (:pass (methods clojure.test/report))] | |
(defmethod clojure.test/report :pass [m] | |
(println "Yay!") | |
(original-fn m))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment