Database Tests with Rollbacks in Clojure
March 4, 2015
I recently purchased a beta version of Eric Normand’s Intro to clojure.test screencast. In it, he demonstrates how to use the
use-fixtures function to set up and tear down state for tests. The function can be used for
:each test run, or
:once. For example:
(ns myapp.db-test (:require [clojure.test :refer :all] [myapp.db :as db] [myapp.migrations :as migrate)) (def database-url "jdbc:postgresql://localhost/myapp_test") (use-fixtures :once (fn [f] (migrate/-main database-url) (f)))
The above code sets up a fixture that will be run once for the namespace. In this case, it will run our database migrations.
While working on a Clojure side project recently, I needed a way to reset a database to a known state after each test was run. Since I was using Clojure’s JDBC library, I knew that I could use transactions, and have each test roll back their changes. This is where
use-fixtures and its
:each option comes in handy.
(ns myapp.db-test (:require [clojure.test :refer :all] [clojure.java.jdbc :as jdbc] [myapp.db :as db] [myapp.migrations :as migrate)) (declare ^:dynamic *txn*) (def database-url "jdbc:postgresql://localhost/myapp_test") (use-fixtures :once ;; same as above) (use-fixtures :each (fn [f] (jdbc/with-db-transaction [transaction database-url] (jdbc/db-set-rollback-only! transaction) (binding [*txn* transaction] (f)))))
Using the above setup, any tests in the
myapp.db-test namespace will be executed in the context of a transaction. Regardless of whether the test passes or fails, changes to the database will be rolled back. For a more complete example, check out a web app I’ve been building to learn more about Clojure.