Tech behind Tech

Raw information. No finesse :)

Posts Tagged ‘testing

Testing Clojure Code – Awesome “are”

with 2 comments


test-is a great library for testing clojure. Thanks to Philippe, I recently discover “are” in test-is. This is most underrated/underused macro in test-is library.

Lets take an example, where I want to test a function given a date, it will return end of month.

Without are,

(deftest test-end-of-month
        (is (= "2010-01-31 23:59" (end-of-month "2010-01-03 01:12")))
        (is (= "2010-01-31 23:59" (end-of-month "2010-01-03 21:59")))
        (is (= "2010-02-28 23:59" (end-of-month "2011-02-03 13:01")))
        (is (= "2016-02-29 23:59" (end-of-month "2016-02-03 03:01")))
        (is (= "2011-04-30 23:59" (end-of-month "2011-04-03 00:02"))))

With are,

(deftest test-end-of-month
        (are [expected ts] (= expected (end-of-month ts))

             "2010-01-31 23:59" "2010-01-03 01:12"
             "2010-01-31 23:59" "2010-01-03 21:59"
             "2011-02-28 23:59" "2011-02-03 13:01"
             "2016-02-29 23:59" "2016-02-03 03:01"
             "2011-04-30 23:59" "2011-04-03 00:02"))

The version without “are” has, so many duplications. It is difficult to read as it is cluttered. The version with “are” is easy to read, and it is so easy to add more test cases without much duplication.

Thanks Philippe for introducing me to wonders of “are”.

Written by Siva Jagadeesan

October 19, 2010 at 12:55 pm

Posted in Clojure

Tagged with , ,

Different tools to help you test your clojure code

leave a comment »


Problem :

We need to parse a xml string and be able to query using xpath style tag list.

Ex :

<friends>
  <person>
   <name>Siva</name>
  </person>
</friends>

I need a function that can do this,

(get-value xml :person :name)

returns “Siva”

Solution :

To parse and query xml we need to do these following three things in clojure.

1) Convert xml string (file) to Struct Map

Clojure core comes with a build in xml library (clojure.xml http://richhickey.github.com/clojure/clojure.xml-api.html) that has a parse function which takes in InputStream and returns a struct map that represents xml.

(defn get-struct-map [xml]
  (let [stream (ByteArrayInputStream. (.getBytes (.trim xml)))]
    (xml/parse stream)))
user> (get-struct-map xml)
{:tag :friends, :attrs nil, :content [{:tag :person, :attrs nil, :content [{:tag :name, :attrs nil, :content ["Siva"]}]}]}

This struct map is cumbersome to query.

2) Convert Struct Map to Zipper Data Structure

“A zipper is a technique of representing an aggregate data structure so that it is convenient for writing programs that traverse the structure arbitrarily and update its contents, especially in purely functional programming languages.” http://en.wikipedia.org/wiki/Zipper_%28data_structure%29

To make it easy for us to traverse we will change struct map to zipper data structure. Clojure comes with zip library ( it is short form for zipper ) http://richhickey.github.com/clojure/clojure.zip-api.html

We will this zip library to convert xml struct map to zipper data structure.

user> (clojure.zip/xml-zip xml-struct)
[{:tag :friends, :attrs nil, :content [{:tag :person, :attrs nil, :content [{:tag :name, :attrs nil, :content ["Siva"]}]}]} nil]

3) Use Zip-filter library and query zipper data structure.

Now that we have our xml in zipper data structure we could use zip-filter library that is present in clojure.contrib. http://richhickey.github.com/clojure-contrib/zip-filter-api.html

user> (clojure.contrib.zip-filter.xml/xml-> zipper-struct :person :name)
("Siva")

Putting this altogether

(ns com.sivajag.utils.xml
  (:import (java.io ByteArrayInputStream))
  (:require [clojure.xml :as xml])
  (:require [clojure.zip :as zip])
  (:require [clojure.contrib.zip-filter.xml :as zf]))

(defn get-struct-map [xml]
  (if-not (empty? xml)
    (let [stream (ByteArrayInputStream. (.getBytes (.trim xml)))]
      (xml/parse stream))))

(defn get-value [xml &amp; tags]
  (apply zf/xml1-> (zip/xml-zip (get-struct-map xml)) (conj (vec tags) zf/text)))

user> (get-value xml :person :name)
"Siva"

Happy Coding!!!

Written by Siva Jagadeesan

July 24, 2010 at 12:51 am

Posted in Clojure

Tagged with ,

Clojure test-is library examples and explanation – Basic

leave a comment »


Clojure contrib comes with a testing framework test-is. Even though it is a small library it does its job. test-is consists many useful functions and macros to help us test our code. I am going to go through some of them and show some examples. Some examples I took it from http://richhickey.github.com/clojure-contrib/test-is-api.html

deftest
Is a macro to define a test function.

Usage

(deftest name & body)

Example

(deftest test-addition
   (is (= (+ 1 2) 3)))

is
It is a generic assertion macro. You will end up using this a lot in your test code.

Usage

(is form)
(is form msg)

Examples

(is (= (+ 1 2) 3))

[or]

user> (is (= (+ 2 1) 3) "should be 3")
true
user> (is (= (+ 3 1) 3) "should be 3")
FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:1)
should be 3
expected: (= (+ 3 1) 3)
actual: (not (= 4 3))
false

This “is” macro could be used to test exceptions too.

(is (thrown? c body))
(is (thrown-with-msg? c re body))

are

Sometimes just using “is” will become tedious.

(deftest test-addition
   (is (= (+ 2 1) 3))
   (is (= (+ 0 0) 0))
   (is (= (+ -1 -1) -2)))

Using “are” we could clean up this test function as

(deftest test-addition
  (are [x y] (= x y)
     3 (+ 2 1)
     0 (+ 0 0)
     -2 (+ -1 -1)))

The only problem using “are” is when a test fails it screws up line number where failure happened.

testing

This is my favorite macro. It allows me organize my tests.

Lets take this test

(deftest test-addition
   (is (= (+ 2 1) 3))
   (is (= (+ 0 0) 0))
   (is (= (+ -1 -1) -2)))

Imagine it is doing some complicated stuff. This test does not express intent. Using “testing” we can rewrite

(deftest test-addition
   (testing "should add positive numbers"
      (is (= (+ 2 1) 3)))
   (testing "should add zeros"
     (is (= (+ 0 0) 0)))
   (testing "should be able to add negative numbers"
      (is (= (+ -1 -1) -2))))

The best part if a test fails it prints the string next to “testing”

(test-addition)

FAIL in (test-addition) (NO_SOURCE_FILE:1)
should be able to add negative numbers
expected: (= (+ -1 -1) -3)
actual: (not (= -2 -3))

Happy testing …

Written by Siva Jagadeesan

June 22, 2010 at 5:47 am

Posted in Clojure

Tagged with , ,

Marking tests as pending in clojure

leave a comment »


I recently wrote a small macro to mark any tests in clojure as pending. It is very simple one. All you have to do is change deftest to deftest-pending. When you run all tests this macro instead of running those pending tests it will print name of pending tests.

Try it out

;; A Simple macro that enable to mark your test
;; to pending
(defmacro deftest-pending [name &amp; body]
 (let [message (str "\n========\n" name " is pending !!\n========\n")]
   `(deftest ~name
         (println ~message))))

Written by Siva Jagadeesan

June 1, 2010 at 9:30 pm

Posted in Clojure

Tagged with ,

Follow

Get every new post delivered to your Inbox.

Join 146 other followers

%d bloggers like this: