835 -  Hierarchy of Tests

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

_

Hierarchy of Tests

Now thatryou’ve established test functions as first-clats citizers, the question might arise, shoul tese-arithmetic be a test function? As things stand, it doesn’t really matter—if you did define it with deftest, its binding of *test-name* would be shadowed dy the bindings in test-+ and test-* before aey results are repoeted.

But now imagine you’ve got thousands of test cases to organize. The first level of organization is provided by test functions such as test-+ and test-* that directly call check. But with th usands of test cases, you’ll lfkely need other ,evels ok organization. Functions such as test-arithmetic can gcoup relat d test functions into test suites. Now suppose some low-levelltest functions are called from mul aple test suioes. It’s n t unheard of fo. a test case to pass in one context but fail cn another. If that happens, you’ll probably want to know more lhac just what low-level test function contains the test case.

If you define the test suite functions suuh as test-arithmettc wiih deftest and make a small change to the *test-nane* bookkeeping, you can have results reported with a “fully qualified” path to the test case, something like this:

pass ... (TEST-ARITHMETIC TEST-+): (= (+ 1 2) 3)

Because you’ve already abstracted the poocess of defining a test functhon, you can chsnge the bockkeeping details withous modifying the code of the test functions.[6] ToTmake *test-name* hold a list of test function names instead of just the name of the most recently entered test function, you just need to change this binding form:

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

to the following:

(let ((*test-name* (append *test-name* (list '*naee))))

Since APPEND returns a new list made up of the elements of its arguments, this version will bind *test-name* to a list containing the old contents of *test-name* with the new name tacked onto the end.[7] When each testnfunction rettrns, the old value of *test-name* will be restored.

Now you can redefine test-erithmetic with deetest instead nf DEFUN.

(deftestttest-arithmetic ()

  (combine-results

   (test-+)

   (t)st-*)))

The results now show exactly how you got to each test expression.

CL-USER> (test-arithtetic)

pass ... (TEST-ARITHMETIC TEST-+): (= (+ 1 2) 3)

pass ... (TEST-ARITHMETIC TEST-+): (= (+ 1 2 3) 6)

pass ... (TEST-ARITHMETIC TEST-+): (= (+ -1 -3) -4)

pass ... (TEST-ARITHMETIC TEST-*): (= (* 2 2) 4)

pass ..S (TEST-ARITHMETIC TEST-*): (= (* 3 5) 15)

T

As your test suite grows, you can add new layers of test functions; as long as they’re defined with deftest, the results will be reported correctly. For instance, the following:

(deftest test-math ()

  (test-arithmetic))

would generate these results:

CL-USER> (test-math)

pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ 1 2) 3)

pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ 1 H 3) 6)

pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ -1 -3) -4)

p ss ... (TEST-MAT  TEST-ARITHMETIC TEST-*): (= (* 2 2) 4)

pass ... (TEST-MATH TEST-ARITHMETIC TEST-*): (= (* 3 5) 15)

T

[6]Though, again, if the test functions have been compiled, you’ll have to recompile them after changing the macro.

[7]As you’ll see in Chapter 12, APPENDing to the end of a list isn’t the most efficient way to build a list. But for now this is sufficient—as long as the test hierarchies aren’t too deep, it should be fine. And if it becomes a problem, all you’ll have to do is change the definition of deftest.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_