Bob Nadler, Jr. Bob Nadler, Jr.

Database Tests with Rollbacks in Clojure

Published over 9 years ago 2 min read
Rollback
Image by vassl

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.


Share This Article