clojureclojure.testclojure-testing

How to disable test fixtures when no tests are running in the current namespace?


I have seen many clojure projects that disable integration tests by default by adding this setting to project.clj:

:test-selectors {:default (complement :integration)
                 :integration :integration}

But, if a namespace contains only integration tests, the fixtures in it still run when I run lein test!

For example if I run lein new app test and make the contents of core_test.clj this:

(defn fixture [f]
  (println "Expensive setup fixture is running")
  (f))
(use-fixtures :once fixture)

(deftest ^:integration a-test
  (println "integration test running"))

Then when I run lein test I see the fixture running even though no tests are run.

What is the correct way to handle this in clojure?


Solution

  • One way to accomplish not running the expensive computation is to take advantage of the fact that even though the :once fixtures will run regardless of whether there are tests to run in the ns or not, the :each fixtures will only run on each actually-running test.

    Instead of doing the actual computation (or acquiring resources like a db connection, or doing whatever side-effects) in the :once fixture, we only do it in the first (we want to do it only once!) :each fixture, for example doing as following:

    (def run-fixture? (atom true))
    
    (defn enable-fixture [f]
      (println "enabling expensive fixture...")
      (try
        (f)
        (finally (reset! run-fixture? true))))
    
    (defn expensive-fixture [f]
      (if @run-fixture?
        (do
          (println "doing expensive computation and acquiring resources...")
          (reset! run-fixture? false))
        (println "yay, expensive thing is done!"))
      (f))
    
    (use-fixtures :once enable-fixture)
    (use-fixtures :each expensive-fixture)
    
    (deftest ^:integration integration-test
      (println "first integration test"))
    
    (deftest ^:integration second-integration-test
      (println "second integration test"))
    

    The output of lein test will be as following (notice how the enable-fixture has run but not the expensive expensive-fixture):

    › lein test
    
    lein test fixture.core-test
    enabling expensive fixture...
    
    Ran 0 tests containing 0 assertions.
    0 failures, 0 errors.
    

    When running lein test :integration, the expensive-fixture will run exactly once:

    › lein test :integration
    
    lein test fixture.core-test
    enabling expensive fixture...
    doing expensive computation and acquiring resources...
    first integration test
    yay, expensive thing is done!
    second integration test
    
    Ran 2 tests containing 0 assertions.
    0 failures, 0 errors.