Refactoring Test Lib
After learning a little more, I decided to apply what I have learnt to make our barebones testing library even more minimal.
Before (LOC 14):
(defmacro make-test (name predicate)
`(lambda ()
(let ((p ,predicate))
(if p
(format t "Test `~A` passed~%" ,name)
(format t "Test `~A` failed~%" ,name))
p)))
(defun run-tests (&rest tests)
(let* ((score (lambda (p) (if p 1 0)))
(run-test (lambda (test) (funcall test)))
(scores (map 'list score (map 'list run-test tests)))
(num-tests (length scores))
(num-passes (reduce #'+ scores)))
(format t "~A out of ~A tests passed~%" num-passes num-tests)))
After (LOC 12):
(defmacro make-test (name predicate)
`(lambda ()
(values ,name ,predicate)))
(defun report-test (name passp)
(format t "Test `~A` ~:[failed~;passed~]~%" name passp))
(defun run-test (test)
(multiple-value-bind (name p) (funcall test)
(apply #'report-test `(,name ,p)) p))
(defun run-tests (&rest tests)
(let ((outcomes (map 'list #'run-test tests)))
(format t "~A out of ~A tests passed~%" (count t outcomes) (length outcomes))))
Even though lines of code didn’t change much, I love the new version for a number of reasons.
- Test reporting is now a separate function, and it uses
format
’s conditional formatting. - Use of
values
to return multiple values andmultiple-value-bind
to consume them. - Use of backtick to quote a list while forcing evaluation of
name
andp
versus the more verbose form:(list name p)
. - No mapping of boolean to 1 or 0 and counting scores, simply using
count
to count number of true values.