diff options
-rw-r--r-- | README.md | 106 |
1 files changed, 104 insertions, 2 deletions
@@ -1,9 +1,111 @@ +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. Signal errors on compilation if they do not pass. +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+ fibble (x y &key (z 10)) + "Hey, a docstring." + :tests + (= (1 2) 13) + (>= (1 2 :z 1) -5) + (:outputp (0 0 :z 0) (lambda (result) (equalp result 0))) + (: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 (>= (fibble 1 2 :z 1) -5)) + + +## additional tests + + +In addition to basic assertions on the output of functions, testire +supports additional 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 + +- `(: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. -A work in progress. +For example + (defvar *count*) + + (defun+ 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+ 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. |