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!
Afficher les réponses par date
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 ,@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