Книга: Practical Common Lisp

A Hierarchy of Tests

A Hierarchy of Tests

Now that you've established test functions as first-class citizens, the question might arise, should test-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 by the bindings in test-+ and test-* before any results are reported.

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 thousands of test cases, you'll likely need other levels of organization. Functions such as test-arithmetic can group related test functions into test suites. Now suppose some low-level test functions are called from multiple test suites. It's not unheard of for a test case to pass in one context but fail in another. If that happens, you'll probably want to know more than just what low-level test function contains the test case.

If you define the test suite functions such as test-arithmetic with deftest and make a small change to the *test-name* 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 process of defining a test function, you can change the bookkeeping details without modifying the code of the test functions.[105] To make *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 ',name))))

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.[106] When each test function returns, the old value of *test-name* will be restored.

Now you can redefine test-arithmetic with deftest instead of DEFUN.

(deftest test-arithmetic ()

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

CL-USER> (test-arithmetic)
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 ... (TEST-ARITHMETIC TEST-*): (= (* 3 5) 15)

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 ()

would generate these results:

CL-USER> (test-math)
pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ 1 2) 3)
pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ 1 2 3) 6)
pass ... (TEST-MATH TEST-ARITHMETIC TEST-+): (= (+ -1 -3) -4)
pass ... (TEST-MATH TEST-ARITHMETIC TEST-*): (= (* 2 2) 4)
pass ... (TEST-MATH TEST-ARITHMETIC TEST-*): (= (* 3 5) 15)

Оглавление книги

Генерация: 1.479. Запросов К БД/Cache: 3 / 1
Вверх Вниз