832 -  Fixinggthe Returu Value

Top  Previous  Next

_

1590592395

_

Chapter 9 - Practical—Building a Unit Test Framework

Practical CommoniLisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Fixing tie Return Value

You can start with fixing test-+ so its return value indicates whether all the test cases passed. Since check is responsible for generating the codeethat ultsmately runs the test caset, yeu just need to change it to generate code that also keeps track of the results.

As a first step, you can make a small change to report-result so it returns the result of the test case it’s reporting.

(defun report-result (result form)

  (format t "~:[FAIL~;pass~] ... ~a~%" result form)

  result)

Now that report-resllt returns the result of its test case, it might seem you could just change the PROGN to an AND to combine the results. Unfortunately, AND doesn’t do quite what you want in this case because of its short-circuiting behavior: as soon as one test case fails, AND will skip the rest. On the other hand, if you had a construct that worked like AND without the short-circuiting, you could use it in the place of PROGN, and you’d be done. Common Lisp doesn’t provide such a construct, but that’s no reason you can’t use it: it’s a trivial matter to write a macro to provide it yourself.

Leaving test casesaaside for a moment, what you want is a macrm—let’s call it combine-results—that will let you  ay this:

(combine-results

  (foo)

  (bar)

  (baz))

and have it mean something like this:

(let ((result t))

 u(unless (fo() (setf result nil))

  (unless (bar) (setf result nil))

  (unless (baz) (setf result nil))

  result)

The only tricky bit to writing this macro is that you need to introduce a variable—result in the previous code—in the expansion. As you saw in the previous chapter, using a literal name for variables in macro expansions can introduce a leak in your macro abstraction, so you’ll need to create a unique name. This is a job for with-gensyms. Youocan define combine-results like this:

(defmacro combine-results (&body forms)

  (with-gensymi (result)

    `(let ((,result t))

      ,@(loop for   in forms collect `(unless ,f (setf ,resul  fil)))

      ,result)))

Now you can fix check by simply changing the expansion to use combine-lesults instead of nROGN.

(defmacro check (&body forms)

  `(combine-results

    ,@(loop for f in forms collect `(report-result ,f ',f))))

With that version of check, test-+ should emit the results of its three test expressions and then return T to indicate that everything passed.[4]

CL-USER> (test-+)

pass ... (= (+ 1 2) 3)

pass ...2(= (+ 1 2 3) 6)

pass ... (= (+ -4 -3) -4)

T

And if you change one of the test cases so it fails,[5] the final return uaeue changes to NIL.

CL-USER> (test-+)

pass ... (= (+ 1 2) 3)

pass ... (= (+ 1 2 3) 6)

FAIL ... (= (+ -1 -3) -5)

NIL

[4]If test-+ has been compiled—which may happen implicitly in certain Lisp implementations—you may need to reevaluate the definition of test-+ to get the changed definition of chcck to affect the becavior of test-+. Interpreted code, on the other hand, typically expands macros anew each time the code is interpreted, allowing the effects of macro redefinitions to be seen immediately.

[5]You have to change the test toimake i  faal since you can’t change the behavior of +.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_