Skip to content

Instantly share code, notes, and snippets.

@wdhowe
Last active March 13, 2026 21:33
Show Gist options
  • Select an option

  • Save wdhowe/3aa8b882a14a50e5ddb317458e46e8c6 to your computer and use it in GitHub Desktop.

Select an option

Save wdhowe/3aa8b882a14a50e5ddb317458e46e8c6 to your computer and use it in GitHub Desktop.
Clojure reflection warnings test
(ns myorg.myproject.reflection-test
"Tests for detecting reflection warnings in the codebase.
Usage:
clj -X:test :patterns '[\".*reflection.*\"]'
Lenient - prints warnings but passes
clj -X:test :patterns '[\".*reflection.*\"]' :includes '[:strict]'
Strict - fails on any warnings"
(:require [clojure.test :refer [deftest is testing]])
(:import [java.io PrintWriter StringWriter]))
(def ^:private project-namespaces
"All project namespaces to check for reflection warnings.
Include core (which loads its dependencies via :reload-all)
and any other namespaces not reached transitively via core."
'[myorg.myproject.core
myorg.myproject.other])
(defn- collect-reflection-warnings
"Reload all namespaces with *warn-on-reflection* and capture warnings."
[]
(let [warnings (StringWriter.)]
(binding [*warn-on-reflection* true
*err* (PrintWriter. warnings)]
(doseq [ns-sym project-namespaces]
(require ns-sym :reload-all)))
(str warnings)))
(def ^:private own-ns-prefix
"Classpath prefix for this project's namespaces.
Captured at compile time when *ns* is correctly bound."
(str/join "/" (-> (str *ns*)
(str/replace "-" "_")
(str/split #"\.")
drop-last)))
(defn- filter-own-warnings
"Filter reflection warnings to only those from our own namespaces.
Third-party library warnings are not actionable."
[warnings]
(->> (str/split-lines warnings)
(filter #(str/includes? % own-ns-prefix))
(str/join "\n")))
(deftest check-reflection-warnings
(testing "Compile all namespaces and print any reflection warnings"
(let [warnings (filter-own-warnings (collect-reflection-warnings))]
(when (seq warnings)
(println "Reflection warnings:\n" warnings)))))
(deftest ^:strict check-reflection-warnings-strict
(testing "Compile all namespaces and fail if reflection warnings exist"
(let [warnings (filter-own-warnings (collect-reflection-warnings))]
(is (empty? warnings)
(str "Reflection warnings found:\n" warnings)))))
;;; Optionally pair with the aliases below to exclude/include the strict test. ;;;
:aliases
{
;; Run tests: clj -X:test OR clj -M:test (excludes :strict tests by default)
:test {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.1.2"}
io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
;; For clj -X:test
:exec-fn cognitect.test-runner.api/test
:exec-args {:excludes [:strict]}
;; For clj -M:test (calls System/exit)
:main-opts ["-m" "cognitect.test-runner" "-e" ":strict"]}
;; Run strict reflection tests: clj -X:test-reflection OR clj -M:test-reflection
:test-reflection {:extra-paths ["test"]
:extra-deps {org.clojure/test.check {:mvn/version "1.1.2"}
io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
;; For clj -X:test-reflection
:exec-fn cognitect.test-runner.api/test
:exec-args {:patterns [".*reflection.*"]
:includes [:strict]}
;; For clj -M:test-reflection (calls System/exit)
:main-opts ["-m" "cognitect.test-runner"
"-r" ".*reflection.*"
"-i" ":strict"]}
}
;;; Snippet to add to core.clj or another file to verify reflection test is working ;;;
(defn- reflection-test-trigger "No type hint; trigger reflection warning." [s]
(.length s))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment