· clojure

Clojure: Unit testing in the REPL

One thing which I think is great about coding with F# is the quick feedback that we can get by defining and then testing out functions in the REPL.

We can do the same thing in Clojure but it’s even better because we can also define and run unit tests which I think is pretty neat.

Nurullah Akkaya has a good post which describes how to use clojure.test, a testing framework written by Stuart Sierra so I’ve been using that to define some tests cases for the little RSS feed parser that I’m writing.

To use clojure.test straight out the box you need the latest version of the clojure source code as Stuart Sierra points out on his website.

I ran the ant task for the project and then launched the REPL pointing to the 'alpha snapshot' jar instead of the '1.0.0' jar and it seems to work fine.

I managed to break the 'get-title' function while playing with it before so I thought that would be a good one to try out the tests in the REPL with.

This function is supposed to strip out the name and the following colon which appears in every title and just show the title of the blog post.

I originally had this definition:

(defn get-title [title]
  (second (first (re-seq #".*:\s(.*)" title))))

I hadn’t realised that this strips from the last colon in the string and therefore returns the wrong result for some inputs.

I created the following tests:

(use 'clojure.test)
(deftest test-get-title
  (is (= "Clojure - It's awesome"  (get-title "Mark Needham: Clojure - It's awesome")))
  (is (= "A Book: Book Review" (get-title "Mark Needham: A Book: Book Review"))))

We can run those with the following function:

(run-tests)
FAIL in (test-get-title) (NO_SOURCE_FILE:19)
expected: (= "A Book: Book Review" (get-title "Mark Needham: A Book: Book Review"))
  actual: (not (= "A Book: Book Review" "Book Review"))

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

Changing the function helps solve the problem:

(defn- get-title [title]
  (second (first (re-seq #"[a-zA-Z0-9 ]+:\s(.*)" title))))
Ran 1 tests containing 2 assertions.
0 failures, 0 errors.

We can also run the assertions directly without having to call 'run-tests':

(is (= "A Book: Book Review" (get-title "Mark Needham: A Book: Book Review")))
true
(is (= "Something Else" (get-title "Mark Needham: A Book: Book Review")))
expected: (= "Something Else" (get-title "Mark Needham: A Book: Book Review"))
  actual: (not (= "Something Else" "A Book: Book Review"))
false

Nurullah has more detail in his post about how to integrate tests into a build although I don’t need to do that just yet!

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket