blob: cc6a2446045be777c1a916492c68137d81e2f94d (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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. 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 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 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
- `(: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.
|