A [testiere](https://www.vocabulary.com/dictionary/testiere) is armor for the head of a horse. Testiere is a Common Lisp library for putting "protection" (tests) in the "head" of functions. # testiere Embed tests inside function defintions. Tests are run when you compile a function, signalling errors when they fail. Great for running tests during interactive development! A work in progress. But here is the basic idea: (defun/t sum-3 (x y &key (z 10)) "Sums three numbers, Z has a default value of 10" :tests (= (1 2) 13) ; (sum-3 1 2) == 13 (= (1 2 :z 3) 6) ; (sum-3 1 2 :z 3) == 6 (:output (0 0) ; tests that (sum-3 0 0) passes the predicate (lambda (result) (= 10 result))) (:fails ; ensures that (sum-3 "strings" "ain't" :z "numbers") fails ("strings" "ain't" :z "numbers")) :end (+ x y z)) Here we define a function with four embedded tests. If any of these tests fail, *the function is not defined*. If the funtion is already defined, it is not redefined. ## basic tests The most basic tests look like (COMPARATOR (ARG1 ARG2 ...) VALUE) Which will call the function with the provided arguments are compare it to a VALUE. For example, `(>= (1 2 :z 1) -5)` runs the test (assert (>= (sum-3 1 2 :z 1) -5)) ## additional tests In addition to basic assertions on the output of functions, testire supports more elaborate vocubulary for embedding tests. Most of these look like: (KEYWORD (ARG1 ARG2 ...) TERM) Where `TERM` varies according to the `KEYWORD` supplied. A few of these are - `(:program FUNCTION-NAME ARGS...)` runs a funcion named FUNCTION-NAME with arguments ARGS. This function is meant to act as a test suite for the function being defined with defun/t. It may call that function and ASSERT things about it. - `(:outputp (..ARGS...) PREDICATE)` asserts that the output passes the one-argument predicate. - `(:afterp (...ARGS...) THUNK)` asserts that the thunk should return non-nil after the function has run. Good for testing values of dynamic variables that the function might interact with. - `(:fails (...ARGS...))` asserts that the function will produce an error with the given arguments. - `(:signals (...ARGS...) CONDITION)` where `CONDITION` is the name of a condition. Asserts that the function will signal a condition of the supplied type when called with the provided arguments. ## Stubs and Bindings Tests can also embed bindings or mock-functions aka stubs. Binding variables looks like (:with-bindings LET-BINDINGS TESTS) and are useful for binding dynamic variables for use during a set of tests. For example (defvar *count*) (defun/t increment-count () "Increments the *count* variable." :tests (:with-bindings ((*count* 4)) (:afterp () (lambda () (= *count* 5))) ; 5 after the first call (= () 6) ; 6 after the second (:outputp () (lambda (x) (= x 7)))) ; and 7 after the third :end (incf *count*)) The `:with-stubs` form is similar, except that it binds temporary values to functions that might be called by the form in questions. Useful for mocking. (defun just-a-function () (print "Just a function.")) (defun/t call-just-a-function () "Calls JUST-A-FUNCTION." :tests (:with-stubs ((just-a-function () (print "TEMP JUST-A-FUNCTION."))) (equal () "TEMP JUST-A-FUNCTION.")) :end (just-a-function)) In the above, the temporary redefinition of JUST-A-FUNCTION is used. ## `WITH-STUBS` and `:PROGRAM` Style Tests By using the `:program` test and the `with-stubs` macro, you can define just about any kind of test. In the following, you use `with-stubs` to mock a function that makes an http request and returns a string. It is assumed that `make-drakma-request` is defined somewhere else and performs some network IO. It is stubbed here so that we do not need to actually access the network. (defun test-url-word-counter () "Stub the function that makes requests that word-counter uses, and then test word counter." (with-stubs ((make-drakma-request () "one two three four five six seven")) (assert (= (count-words) 7)))) (defun/t count-words () "Fetches a url and counts now many words the page contains." :tests (:program test-url-word-counter) :end (let ((fetched (make-drakma-request))) (1+ (count #\space fetched)))) If `test-url-word-counter` fails, `count-words` isn't defined.