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
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:
((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))))))
For a CommonLisp
(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 UnitTest
ing 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.
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.