[gambit-list] Unit testing fun
Marc Feeley
feeley at iro.umontreal.ca
Sun May 3 00:40:59 EDT 2009
On 1-May-09, at 4:40 AM, lowly coder wrote:
> Hi Gambiters,
>
> I've been thinking for a long time about the simplest unit testing
> framework I can think of. I came up with the following:
>
> ~/testing$ cat unit-test.scm
> (define (unit-test-create name)
> (let ((unit-test-name name)
> (tests '()))
>
> (define (add name test)
> (set! tests (cons (cons name test) tests)))
>
> (define (run-tests)
> (map (lambda (value)
> (let ((x (car value))
> (y (cdr value)))
> (pp `(,x ,(y)))))
> (reverse tests)))
>
> (define (dispatch . cmds)
> (cond ((eq? (car cmds) 'run) (run-tests))
> ((eq? (car cmds) 'add) (add (cadr cmds) (caddr cmds)))
> ((#t (error ,(command ,cmds not supported))))))
>
> dispatch))
> ~/testing$ cat a.scm
> (include "unit-test.scm")
>
> (define unit-tests (unit-test-create 'tests-a))
>
> (unit-tests 'add '+ (lambda () (eq? (+ 1 1) 2)))
> (unit-tests 'add '- (lambda () (eq? (- 1 1) 0)))
> (unit-tests 'add '* (lambda () (eq? (* 1 1) 2)))
>
> ~/testing$ gsi -e "(load \"a.scm\") (unit-tests 'run)"
> (+ #t)
> (- #t)
> (* #f)
>
>
>
> I'm almost happy with this, except that if I have a.scm and b.scm, I
> have to have different names in them (I can't have them both use
> 'unit-tests').
>
> Maybe I can call them 'unit-tests-a' or 'unit-tests-b' .. but then I
> have to deal with folder path.
>
> Maybe I can call them 'unit-tests-testing-a' and 'unit-tests-testing-
> b' but this becomes a pain when I move files around.
>
> What I want would be somehow, to have a variable created
> automatically that's _local_ to the particular file, so that I can
> still run something like:
>
> gsi -e "(load \"a.scm\") (magic)"
> or
> gsi -e "(include \"a.scm\") (magic)"
> or
> gsi -e "(magic1 \"a.scm\") (magic2)"
>
> and have the proper tests run.
>
> How can I do this?
>
> Thanks!
Add this to unit-test.scm:
(##define-syntax define-local
(lambda (src)
(if (not (##source? src))
(error "expected a source object")
(let ((locat (##source-locat src)))
(if (not locat)
(error "location unknown")
(let ((container (##locat-container locat))
(position (##locat-position locat)))
(let* ((path (##container->path container))
(ns (path-strip-extension (path-strip-
directory path)))
(local-namespace (string-append ns "#"))
(pattern (##desourcify (cadr (##source-code
src))))
(id (if (symbol? pattern) pattern (car
pattern)))
(rest (cdr (##source-code src))))
`(begin
(namespace (,local-namespace ,id))
(define , at rest)))))))))
and in a.scm use "define-local" like this:
(define-local unit-tests (unit-test-create 'tests-a))
For your unit test framework, in general equal? seems like a better
choice for testing equality of the result with the expected result (at
least for numbers as in your example).
Also, the use of macros might make for a more elegant expression of
the tests, for example:
(unit-test (equal? 2 (+ 1 1)))
The use of a macro will allow you to display the actual test that
failed. Along the lines of:
(define-macro (unit-test expr)
`(if (not ,expr) (begin (display "failed test: ") (pretty-print
',expr))))
Of course this could be made much more robust (to catch exceptiosn for
example).
Marc
More information about the Gambit-list
mailing list