-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathbiff.go
173 lines (142 loc) · 3.7 KB
/
biff.go
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Package biff provides support for nested testing, useful for complex business
// logic, APIs, and stateful systems.
//
// Typical usage:
// biff.Alternative("Initial value", func(a *A) {
// value := 10
// a.AssertEqual(value, 10)
//
// a.Alternative("Plus 50", func(a *A) {
// // Here value == 10
// value += 50
// a.AssertEqual(value, 60)
// })
//
// a.Alternative("Multiply by 2", func(a *A) {
// // Here value == 10 again (it is an alternative from the parent)
// value *= 2
// a.AssertEqual(value, 20)
// })
// })
//
// Will produce this output:
// Case: Initial value
// value is 10
// Case: Plus 50
// value is 60
// -------------------------------
// Case: Initial value
// value is 10
// Case: Multiply by 2
// value is 20
// -------------------------------
// Case: Initial value
// value is 10
// -------------------------------
//
// Other example:
// func TestMyTest(t *testing.T) {
// Alternative("Login", func(a *A) {
// user := mySystem.Login("[email protected]", "123456")
// a.AssertEqual(user.Email, "[email protected]")
// a.Alternative("Do action 1", func(a *A) {
// // Do something
// })
// a.Alternative("Do action 2", func(a *A) {
// // Do something else
// })
// ...
// }
// }
//
// If some assertion fails, it will print expected value and `file:line` to get
// direct to the line, like this:
//
// Expected: []string{"test a", "test a22"}
// Obtained: []string{"test a", "test a2"}
// at myservice.Test_isolation.func1.2(0xc420094a80 /.../project/item_test.go:21 +0x18
//
package biff
import (
"fmt"
"strings"
)
// An A is a type passed to alternative functions to manage recursion status and
// keep human information information related to the test: `title` and
// `description`.
type A struct {
skip int
f F
substatus *[]int
done bool
// Title is the human readable short description for a test case.
Title string
// Description is an optional human detailed description for a test case,
// needs to be filled inside alternative function.
Description string
}
func newTest(f F) *A {
return &A{
f: f,
}
}
// Alternative describes a new alternative case inside current case. It will be
// executed in a isolated branch.
func (a *A) Alternative(title string, f F) *A {
if a.skip == 0 {
n := newTest(f)
n.Title = title
a.done = n.run(a.substatus)
}
a.skip--
return a
}
func (a *A) run(status *[]int) (done bool) {
fmt.Println("Case:", a.Title)
skip := &(*status)[0]
substatus := (*status)[1:]
// Execute
a.skip = *skip
a.substatus = &substatus
a.f(a)
// There is no more alternatives
if a.skip == 0 {
printDescription(a.Description)
(*status)[0] = 0
return true
}
if a.done {
(*status)[0]++
}
return
}
func trimMultiline(s string) (r string) {
if s == "" {
return
}
for _, line := range strings.Split(s, "\n") {
r += strings.TrimSpace(line) + "\n"
}
return
}
func printDescription(s string) {
fmt.Print(trimMultiline(s))
}
// F is a callback alternative function passed to an `Alternative` with testing
// code.
type F func(a *A)
// Alternative is the root use case and the test runner. It will execute all
// test alternatives defined inside.
func Alternative(title string, f F) {
// Ñap :_(
status := []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
for {
t := newTest(f)
t.Title = title
done := t.run(&status)
fmt.Println("-------------------------------")
if done {
return
}
}
}