Test-file namespace convention

clojure

#1

This question is brought to you by TDD in Cider. I was a Luminus user before I knew what TDD was. Luminus by default builds a structure with /tests/<my-project> as the path, and includes the appropriate things in project.clj to handle this. This makes sense to me, because it means I can easily omit those paths from my production war/jar. However, Cider says that it uses a “leiningen convention” and by default expects the files to be siblings to file they are based upon with a -test.clj filename. I can change the cider function that infers namespaces of tests, but if that -test.clj convention is more prevalent than just cider, it may have unseen merit. How do you arrange your testing namespaces? Do you have separate places for those tests which make sure all the functions work, and those functions which test a certain workflow (e.g. “what a user does”)?


#2

There are two separate aspects at play here. One is how a test namespace’s name relates to the namespace under test’s, the other is where these test namespaces are put on the classpath.

The most common convention by far in the Clojure world (folks correct me if I’m wrong but I hardly ever see anything else) is that a foo.bar namespace has its tests in foo.bar-test. It’s also quite common to name the test for the baz var baz-test, but there you see more variation in how people do it.

(ns foo.bar)

(defn baz [] ,,,)
(ns foo.bar-test
  (:require [clojure.test :refer :all]
            [foo.bar :as bar]))

(deftest baz-test
  (is (= (bar/baz) ,,,)))

Luminus is interesting in that instead of foo.bar-test it uses foo.test.bar, and instead of baz-test it uses test-baz.

(ns foo.test.bar
  (:require [clojure.test :refer :all]
            [foo.bar :as bar]))

(deftest test-baz
  (is (= (bar/baz) ,,,)))

These files follow the Clojure naming conventions, so they end up in foo/bar_test.clj or foo/test/bar.clj, however they are not usually heaped in with the rest of the source files under src. Instead they are commonly placed in test, or test/clj if you have a clj+cljs app. This is also what luminus does, so you end up with for instance

# common convention
test/foo/bar_test.clj

# luminus
test/clj/foo/test/bar.clj

And so you’d have either test or test/clj on the classpath

;; Leiningen
:test-paths ["test/clj"]

;; deps.edn
:paths ["src" "test/clj"]

Personally I’ve been opting to put my main Clojure tests under test/unit, with any supporting code (e.g. custom assertions and test helpers) under test/support. This also leaves space to add other test types or suites like test/cljs, or test/api or test/integration.

(On a side note about testing conventions, I’ve come to always add [clojure.test :refer :all], even though I generally avoid :refer :all anywhere else, but in my tests I want the testing API to always be available, including are, run-tests, etc. In ClojureScript tests this doesn’t work and I either :as t or :refer [all-the-things] ,or both.)


#3

Thanks for that comprehensive overview. It sounds like there’s a good deal of flexibility available to me to choose from, although there are some informal standards. Dmitri, when designing Luminus, has imparted his own conventions (which, as an aside, are not what Cider or CLJ-Refactor seem to consider standard). So basically, I need to put some thought into how I want test structure to be for my teams.