Testing in Clojure (Part-1: Unit testing)


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.


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

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


:plugins [lein-midje "3.1.3"]
:dependencies [midje "1.6.2"]

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.

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

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


(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]
;;-------------------------)

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.


(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"))))

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


;;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.

Another example test case for testing validation method :


(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"))

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


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.

Example Test case for testing nested method by unit testing method


(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")))))

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


lein test example.test.methodTest

Ran 1 tests containing 1 assertions.

0 failures, 0 errors

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’.

This entry was posted in Clojure and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s