Macros and Unit Testing

The compilation of a Scheme program is divided into a macro-expander and evaluator phase. To the macro-expander, the expression '(+ 1 2) is a three-element list containing the symbols +, 1, and 2. To the evaluator, the expression (+ 1 2) is a function application that returns the value 3. The + symbol is now the addition function, and symbols 1 and 2 are now numbers.

Macros are Scheme functions that live in the macro-expander. They are Scheme programs that manipulate other Scheme programs before they are evaluated as programs. To reiterate, an unevaluated Scheme program is only lists and symbols — basic Scheme data types.

(define-syntax assert
  (syntax-rules ()
    [(_ compare x y)
     (let ([computed-x x]
           [computed-y y])
       (unless (compare computed-x computed-y)
         (printf "Test failed:\nlhs: ~a -> ~a, rhs: ~a -> ~a\n"
                 (quote x)
                 computed-x
                 (quote y)
                 computed-y)))]))
A macro for unit testing. Asserts equality.

assert is a macro I wrote for unit testing. It asserts that two computed expressions are comparable. During the macro-expander phase, assert, expands the test expressions into two parts. The first part returns the test expressions as function calls, whereas the second part returns the same test expressions unevaluated — list literals.

If assert were a function and not a macro, it would be unable to manipulate its test expressions in both their evaluated and unevaluated forms.