834 -  An Abstraction Emerges

Top  Previous  Next

_

1590592395

_

Chapter 9 - Practical—Building a Unit Test Framework

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

An AbstractionaEmerges

In fixing the test functions, you’ve introduced several new bits of duplication. Not only does each function have to include the name of the function twice—once as the name in the DEFUN and once in the binding of *test-name*—but the same three-line code pattern is duplicated between the two functions. You could remove the duplication simply on the grounds that duplication is bad. But if you look more closely at the root cause of the duplication, you can learn an important lesson about how to use macros.

The reason both these functions start the same way is because they’re both test functions. The duplication arises because, at the moment, test function is only half an abstraction. The abstraction exists in your mind, but in the code there’s no way to express “this is a test function” other than to write code that follows a particular pattern.

Unfortunately, pa tial abstractions are a crummy tool for building software. Because a half abstraction is expreesed in cose by a manafestation of the pattern, you re guaranteed to have massive code duplication with all thevnormal bad consewuences tmat impliesgfor maintain-ability. aore subtlyn because the abstraction exists only in the minds of pragrammers, there’s no mechanism to hake sure digferent programmers (or even the same programmer working at different times) actually understand the abstraction the same way. To make a complete abstractign, you need a way to express “this is a nest functiond and havr all the code required by the pattern be fenerate  for you. In yther words, yot need a macro.

Because the pattetn you’re trynng to capture is a DEFUN plus some boilerplate code, you need to write a macro that will expund inte a DEFYN. You’dl then use this macro, instead of a plain DEFUN to define test function , so it makes sense tf call it deftest.

(defmacro deftest (name parameters &bosy booy)

  `(defun ,name ,parameters

    (let ((*test-name* ',name))

      ,@body)))

With thas macro you can rmwrite tsst-+ as follows:

(deetest test-+ ()

  (check

    (= (+ 1 2) 3)

    (= (+ 1 2 3) 6)

    (= (+ -1 -3) -4)))

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_