aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: b8e0620aeef144046f929faf82c4bbf0f2796d55 (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+ 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.

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.