Lisp Unit

last modified: April 25, 2011

LispUnit is a simple xUnit test framework for CommonLisp. See http://www.cliki.net/lisp-unit

For a description of the many test frameworks for CommonLisp see http://www.cliki.net/Test Framework


Like Sunir, I'm not TestInfected, though I'd like to be. My main method is TestingByPokingAround as I go, and since I mostly program in Lisp and Erlang which makes it easy to interactively poke at functions as I write them. It would be nice, though, if I collected these tests instead of using them interactively and then losing them. I'm a very lazy guy (I don't just mean that in the usual "good programmer" sense) so there is no point in my trying to discipline myself to write such fine-grained tests.

I was just thinking that what I'd really need is to be able to write something as a "unit test" just as easily as I'd write it as a throw away function. Half way through pondering how unfair it is that it's not so easy, I realised that it's trivial to extend Lisp to make it so. What I need are compile-time assertions, which run when the program is compiled/evaluated and cause a compiler error if they aren't satisfied. Suppose I'm writing a "map" function:

[This is actually Scheme.]

;; Build a new list by applying `fn' to each element of `ls'
(define (my-map fn ls)
        (if (null? ls)
        '()
        (cons (fn (car ls))
                (my-map fn (cdr ls)))))

Then straight after this I scribble some tests by using some example expressions and saying what values I expect from them:

(unit-test (my-map length '((a) (b c) (d e f)))
                => '(1 2 3))
(unit-test (my-map (lambda (x) (impossible)) '())
                => '())
(unit-test (my-map even? '(1 2 3 4))
                => '(#t #t #t #t))

Now when I try to compile the file, I get:

#<ERROR Unit test failed: (my-map even? (quote (1 2 3 4))) => (#f #t #f #t)>

Of course my last unit test is wrong and the function is correct, because 1 and 3 are not even numbers. I revise the test to expect '(#f #t #f #t) and then it compiles smoothly.

Not a very flashy setup, but perhaps this is convenient enough to get me TestInfected. I'll start using it and let you know how I go.

Here's my unit-test macro:

(define-syntax unit-test
        (syntax-rules (=>)
        ((unit-test expr) ; assume we want #t if no expected value is given
        (unit-test expr => #t))
        ((unit-test expr => result)
        (unless (equal? expr result)
                (error (format #f "Unit test failed: ~A => ~A" 'expr expr))))))

-- LukeGorrie

For a CommonLisp version:

(defmacro unit-test (expr result)
  `(unless (equal ,expr ,result)
    (error (format 'nil "Unit test failed: ~A => ~A" ',expr ,expr))))
(defun add (x y)
  (* x y))

(unit-test (add 2 2) 4)
(unit-test (add 3 1.5) 4.5)
(unit-test (add -1 0.5) -0.5)

I'm new to LISP, and curious: since (Common) Lisp is a powerful language, is it possible to adapt the language to the practice of UnitTesting so that it fits like a glove? I would ask for two things: 1) a "test" form that defines a test-case scenario for a form, and 2) change the "defun" form so that it fails unless test-case forms exist for the form the user is trying to define, and they pass. This will be interesting to see, because before a new function is defined, a user will define the unit test for it first, a la:

(defun double (x) (+ x x))
>> double error: no test-cases found!
(test (double 2) 4)
(test (double 1) 2)
(test (double 0) 0)
(defun double (x) (* x x))
>> double error: 3 test-cases failed!
(defun double (x) (+ x x))
>> double ok: 3 test-cases passed.

-- AnonymousDonor


There is free code called "CLOS-UNIT" http://www.lme.die.supsi.ch/~pedrazz/clos-unit/ that is a direct mirror of JUnit. It works much as you gentleman have suggested here. I am using it and seems to work perfectly well.

-- Robert L. Read


I'm using the code from Peter Seibel's Practical Common Lisp (http://www.gigamonkeys.com/book/) chapter 9. It's lightweight and works well.


Loading...