Testing in Clojure (Part-1: Unit testing)

Knoldus Inc.
Knoldus - Technical Insights
3 min readMar 22, 2014

Testing a web application in Clojure is as important as testing in any other language. Clojure testing has been made simpler with its built in unit tests.

This blog explains about how to do unit testing(testing each module independently) of Clojure methods with some very basic syntax.

For Clojure testing we would be using a clojure.test testing namespace to load testing framework for placing the midje framework test cases.

[code language=”scala”]

(ns testing-namespace
(:use clojure.test))

[/code]

Firstly, add the following dependencies and plugins of Midje testing framework in your project.clj file:

[code language=”scala”]

:plugins [lein-midje “3.1.3”]
:dependencies [midje “1.6.2”]

[/code]

testing framework gives you choice from using any of the two test defining methods:

  1. defining test using with-test followed by method defination and test case.

[code language=”scala”]

(with-test
(defn sum[s]
(+ s 2))
(is (= 3 (sum 1)))

[/code]

2. or defining your test cases in a seperate file using deftest macro(test function with no arguments) which improves the code quality.

Example: create two separate files-

method.clj

[code language=”scala”]

(ns method.routes.core)

(defn truthy? [value]
(if (= value “true”)
true
false))

(defn valid? [id email]
(vali/rule (vali/has-value? id)
[:id “Username required”])
(vali/rule (vali/is-email? email)
[:email “Invalid Email Address”
])

(defn change-profile [old-password new-password confirm-password user]
(if (password-match? old-password user)
(if (confirmation-check? new-password confirm-password)
(do
— — — — — — -
(profile confirm-password))
(do
(vali/rule false
[:no-match “Password does not match”])
(profile old-password))))

(defn password-match[ old-password user]
;; — — — — — — — — — — — — )

(defn confirmation-check[new-password confirm-password]
;; — — — — — — — — — — — — -)

[/code]

methodTest.clj (Test file)

below namespace definition also requires importing the namespace of the file where the methods to be tested are defined followed by :refer :all for refering to every method in this namespace. Noir.util.test is required only to test those methods which uses clojure noir library validation checks.

[code language=”scala”]

(ns example.test.methodTest
(:use clojure.test
example.handler
noir.util.test)
(:require [method.routes.core :refer :all]))
(deftest truthy-testing
;;for true test case:
(is(truthy? “true”)))
;;for false test case:
(is(truthy? “false”))))

[/code]

(for leiningen clojure framework we are using lein test command in terminal within project name path to run the test.)

[code language=”scala”]

;;output for true test case i.e; no exception shown by passing expected value same as actual
lein test example.test.methodTest
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
;; output for false test case i.e;failure occured by passing expected value not same as actual
FAIL in (truthy-testing) (methodTest.clj:9)
expected: (truthy? “false”))
actual: (not (truthy? “false”))

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

[/code]

Another example test case for testing validation method :

[code language=”scala”]

(deftest -validation-test
(is (with-noir (auth/valid? “username” “username@gmail.com”)) “message: valid”)
(is (with-noir (auth/valid? “” “username@gmail.com” )) “message: null value in a string”)
(is (with-noir (auth/valid? “username” “username”)) “message: Invalid Email Address”))

[/code]

output: first assertion shows true with no error message, rest 2 assertion gives described error message and failure test case

[code language=”scala”]

lein test example.test.methodTest
lein test :only example.test.methodTest/validation-test
FAIL in (validation-test) ( methdTest.clj:18)
message: null value in a string
expected: (with-noir (auth/valid? “” “username@gmail.com”))
actual: false

lein test :only example.test.methodTest/validation-test
FAIL in (validation-test) (methodTest.clj:19)
message: Invalid Email Address
expected: (with-noir (auth/valid? “username” “username”))
actual: false

Ran 1 tests containing 3 assertions.
2 failures, 0 errors.
Tests failed.

[/code]

Example Test case for testing nested method by unit testing method

[code language=”scala”]

(deftest change-profile-test
(is (= true (with-noir (password-match? “1234567890” “username”)))”not found in database”)
(is (= true (with-noir (confirmation-check? “1234” “1234”)))”invalid”)
(is (= true (with-noir (update-profile “1234567890” “1234” “1234” “username”)))))

[/code]

output: shows true result with no error message as the same data exists in database for “username”

[code language=”scala”]

lein test example.test.methodTest

Ran 1 tests containing 1 assertions.

0 failures, 0 errors

[/code]

Note:

*(is) in above test cases checks a single instance of method and throws exception if fails.Multiple assertions can also be checked within a single method by just defining multiple arguments and not same method again and again using(are).

*lein test command runs all tests in all namespaces. One can test the test cases under a single namespace also using lein test ‘your.namespace’.

--

--

Knoldus Inc.
Knoldus - Technical Insights

Group of smart Engineers with a Product mindset who partner with your business to drive competitive advantage | www.knoldus.com