Unit testing has become a standard tool for testing an piece of code. Implementing a Scheme solution for unit testing is simple and fun. You define a domain specific language for describing unit tests. This is completly done with macros. It looks something like this:
(define-unit-test testing-string=? "testing simple cases of string=? procedure" (assert-true! (string=? "Hello" "Hello")) (assert-false! (string=? "Hello" "world")))
Basically you specify a unit test name, testing-string=? here, together with a documentation string. Then comes a Scheme code performing the tests. You use the different flavours of the assert-xxx! procedure.
The implementation looks as follow. The main datastructure is the unit-test record type.
(define-record-type(make-unit-test name doc thunk) unit-test? (name unit-test-name set-unit-test-name!) (doc unit-test-doc set-unit-test-doc!) (thunk unit-test-thunk set-unit-test-thunk!))
You should recognize here the different parts of the define-unit-test macro. In fact the macro basically creates instances of the <unit-test> record and store them in a table. Here is the macro definition:
(define-syntax define-unit-test
(syntax-rules ()
((define-unit-test ?name ?doc . ?body)
(add-unit-test! (make-unit-test '?name ?doc (lambda () . ?body))))))
All the unit tests defined are stored in a global variable.
(define *unit-tests* '()) (define (add-unit-test! test) (set! *unit-tests* (cons test *unit-tests*)))
Running all the unit tests defined so far is quite simple:
(define (run-all) (for-each (lambda (t) ((unit-test-thunk t))) *unit-tests*))
Writing unit tests requires the different assert procedures. They are simply implemented as procedures.
(define (assert-true! value)
(if (not value) (display "error: assert-true! violation")))
(define (assert-false! value)
(if value (display "error: assert-false! violation")))
(define (assert! expected effective)
(if (not (equal? expected effective))
(display "error: assert violation!")))
That's it for the implementation. In under 50 lines of Scheme code you have a basic unit testing framework: that's fun or not ? Moreover this is a solution that runs on any R5RS Scheme system with SRFI-9 record type definition: Scheme48, PLT Scheme, MIT-Scheme, etc...
This implementation has some limitations though. You may want some of the following features if you want a solid unit testing framework: