Clojure - tests with components strategy

715 Views Asked by At

I am implementing an app using Stuart Sierra component. As he states in the README :

Having a coherent way to set up and tear down all the state associated with an application enables rapid development cycles without restarting the JVM. It can also make unit tests faster and more independent, since the cost of creating and starting a system is low enough that every test can create a new instance of the system.

What would be the preferred strategy here ? Something similar to JUnit oneTimeSetUp / oneTimeTearDown , or really between each test (similar to setUp / tearDown) ?

And if between each test, is there a simple way to start/stop a system for all tests (before and after) without repeating the code every time ?

Edit : sample code to show what I mean

(defn test-component-lifecycle [f]
  (println "Setting up test-system")
  (let [s (system/new-test-system)]
    (f s) ;; I cannot pass an argument here ( https://github.com/clojure/clojure/blob/master/src/clj/clojure/test.clj#L718 ), so how can I pass a system in parameters of a test ?
    (println "Stopping test-system")
    (component/stop s)))

(use-fixtures :once test-component-lifecycle)

Note : I am talking about unit-testing here.

1

There are 1 best solutions below

0
On

I would write a macro, which takes a system-map and starts all components before running tests and stop all components after testing.

For example:

(ns de.hh.new-test
 (:require [clojure.test :refer :all]
           [com.stuartsierra.component :as component]))


;;; Macro to start and stop component
(defmacro with-started-components [bindings & body]
    `(let [~(bindings 0) (component/start ~(bindings 1))]
       (try
          (let* ~(destructure (vec (drop 2 bindings)))
            ~@body)
       (catch Exception e1#)
       (finally
         (component/stop ~(bindings 0))))))

;; Test Component
(defprotocol Action
  (do-it [self]))

(defrecord TestComponent [state]
   component/Lifecycle
   (start [self]
      (println "====> start")
      (assoc self :state (atom state)))
   (stop [self]
      (println "====> stop"))

   Action
    (do-it [self]
       (println "====> do action")
       @(:state self)))

;: TEST
(deftest ^:focused component-test
   (with-started-components
      [system (component/system-map :test-component (->TestComponent"startup-state"))
       test-component (:test-component system)]

    (is (= "startup-state" (do-it test-component)))))

Running Test you should see the out put like this

====> start
====> do action
====> stop

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.