clojure.test Testing Framework

Overview

Qualified supports clojure.test, the built-in testing framework for Clojure 1.6.0

The following notes are largely derived from: clojure.test - Clojure API Documentation.

Setup

A minimal test fixture looks as follows:

(ns clojure.greeter)

(defn make-greeter [greeter-name]
  (fn [your-name]
    (format "My name is %s! Welcome, %s!"
             greeter-name
             your-name)))
(ns clojure.greeter-test
  (:require [clojure.test :refer :all]
            [clojure.greeter :refer :all]))

(deftest Greetings
  (is
   (= ((make-greeter "Shoki, the Demon Queller")
       "Son Gokū, the Monkey King")
      "My name is Shoki, the Demon Queller! Welcome, Son Gokū, the Monkey King!")))

is

The core of the clojure.test library is the is macro, which lets you make assertions of any arbitrary expression:

(is (= 4 (+ 2 2)))
(is (instance? Integer 256))
(is (.startsWith "abcde" "ab"))

These are all passing tests. They are all expected to output:

Test Passed

The following test won't pass, however:

(deftest Will-Fail
  (is (= 5 (+ 2 2))))

It will output:

Will-Fail
   expected: (= 5 (+ 2 2)) - actual: (not (= 5 4))

The expected: line shows you the original expression, and the actual: shows you what actually happened. In this case, it shows that (+ 2 2) returned 4, which is not = to 5.

There are two special assertions for testing exceptions. The (is (thrown? c ...)) form tests if an exception of class c is thrown:

(is (thrown? ArithmeticException (/ 1 0)))

(is (thrown-with-msg? c re ...)) does the same thing and also tests that the message on the exception matches the regular expression re:

(is (thrown-with-msg? ArithmeticException #"Divide by zero"
                      (/ 1 0)))

Documenting Tests

is takes an optional second argument, a string describing the assertion. This message will be included in the error report.

(is (= 5 (+ 2 2)) "Crazy arithmetic")

In addition, you can document groups of assertions with the "testing" macro, which takes a string followed by any number of assertions. The string will be included in failure reports. Calls to "testing" may be nested, and all of the strings will be joined together with spaces in the final report, in a style similar to RSpec.

(testing "Arithmetic"
  (testing "with positive integers"
    (is (= 4 (+ 2 2)))
    (is (= 7 (+ 3 4))))

  (testing "with negative integers"
    (is (= -4 (+ -2 -2)))
    (is (= -1 (+ 3 -4)))))

Note that, unlike RSpec, the testing macro may only be used INSIDE a deftest.

are

Usage: (are argv expr & args)

Checks multiple assertions with a template expression.

See clojure.template/do-template for an explanation of templates.

Example:

         (are [x y] (= x y)
              2 (+ 1 1)
              4 (* 2 2))

Expands to:

         (do (is (= 2 (+ 1 1)))
             (is (= 4 (* 2 2))))

Note: This breaks some reporting features, such as line numbers.

with-redefs

One of the most powerful tools of testing in clojure is the with-redefs macro, which allows you to mock functions.

This is especially useful when you want to prevent access to built-ins for some exercise. For example, the following disables clojure.core/reverse:

(with-redefs
  [clojure.core/reverse
   (fn [& _]
     (throw (Exception. "Sorry! The reverse built-in is disabled for this challenge!")))]

  (deftest Reversing
    (is (= (reverse [1]) [1]))))

We should expect this test to fail, since it is clearly is using clojure.core/reverse