Text file
talks/2014/testing.slide
1 Testing Techniques
2 Google I/O 2014
3
4 Andrew Gerrand
5 adg@golang.org
6
7 * Video
8
9 This talk was presented at golang-syd in July 2014.
10
11 .link http://www.youtube.com/watch?v=ndmB0bj7eyw Watch the talk on YouTube
12
13
14 * The basics
15
16 * Testing Go code
17
18 Go has a built-in testing framework.
19
20 It is provided by the `testing` package and the `go` `test` command.
21
22 Here is a complete test file that tests the `strings.Index` function:
23
24 .code testing/test1/string_test.go
25
26
27 * Table-driven tests
28
29 Go's struct literal syntax makes it easy to write table-driven tests:
30
31 .code testing/test2/string_test.go /func TestIndex/,/^}/
32
33
34 * T
35
36 The `*testing.T` argument is used for error reporting:
37
38 t.Errorf("got bar = %v, want %v", got, want)
39 t.Fatalf("Frobnicate(%v) returned error: %v", arg, err)
40 t.Logf("iteration %v", i)
41
42 And enabling parallel tests:
43
44 t.Parallel()
45
46 And controlling whether a test runs at all:
47
48 if runtime.GOARCH == "arm" {
49 t.Skip("this doesn't work on ARM")
50 }
51
52
53 * Running tests
54
55 The `go` `test` command runs tests for the specified package.
56 (It defaults to the package in the current directory.)
57
58 $ go test
59 PASS
60
61 $ go test -v
62 === RUN TestIndex
63 --- PASS: TestIndex (0.00 seconds)
64 PASS
65
66 To run the tests for all my projects:
67
68 $ go test github.com/nf/...
69
70 Or for the standard library:
71
72 $ go test std
73
74
75 * Test coverage
76
77 The `go` tool can report test coverage statistics.
78
79 $ go test -cover
80 PASS
81 coverage: 96.4% of statements
82 ok strings 0.692s
83
84 The `go` tool can generate coverage profiles that may be interpreted by the `cover` tool.
85
86 $ go test -coverprofile=cover.out
87 $ go tool cover -func=cover.out
88 strings/reader.go: Len 66.7%
89 strings/strings.go: TrimSuffix 100.0%
90 ... many lines omitted ...
91 strings/strings.go: Replace 100.0%
92 strings/strings.go: EqualFold 100.0%
93 total: (statements) 96.4%
94
95 * Coverage visualization
96
97 $ go tool cover -html=cover.out
98
99 .image testing/cover.png
100
101
102 * Advanced techniques
103
104 * An example program
105
106 *outyet* is a web server that announces whether or not a particular Go version has been tagged.
107
108 go get github.com/golang/example/outyet
109
110 .image testing/go1.1.png
111
112
113 * Testing HTTP clients and servers
114
115 The `net/http/httptest` package provides helpers for testing code that makes or serves HTTP requests.
116
117
118 * httptest.Server
119
120 An `httptest.Server` listens on a system-chosen port on the local loopback interface, for use in end-to-end HTTP tests.
121
122 type Server struct {
123 URL string // base URL of form http://ipaddr:port with no trailing slash
124 Listener net.Listener
125
126 // TLS is the optional TLS configuration, populated with a new config
127 // after TLS is started. If set on an unstarted server before StartTLS
128 // is called, existing fields are copied into the new config.
129 TLS *tls.Config
130
131 // Config may be changed after calling NewUnstartedServer and
132 // before Start or StartTLS.
133 Config *http.Server
134 }
135
136 func NewServer(handler http.Handler) *Server
137
138 func (*Server) Close() error
139
140 * httptest.Server in action
141
142 This code sets up a temporary HTTP server that serves a simple "Hello" response.
143
144 .play testing/httpserver.go /START/,/STOP/
145
146
147 * httptest.ResponseRecorder
148
149 `httptest.ResponseRecorder` is an implementation of `http.ResponseWriter` that records its mutations for later inspection in tests.
150
151 type ResponseRecorder struct {
152 Code int // the HTTP response code from WriteHeader
153 HeaderMap http.Header // the HTTP response headers
154 Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
155 Flushed bool
156 }
157
158 * httptest.ResponseRecorder in action
159
160 By passing a `ResponseRecorder` into an HTTP handler we can inspect the generated response.
161
162 .play testing/httprecorder.go /START/,/STOP/
163
164
165 * Race Detection
166
167 A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.
168
169 To help diagnose such bugs, Go includes a built-in data race detector.
170
171 Pass the `-race` flag to the go tool to enable the race detector:
172
173 $ go test -race mypkg // to test the package
174 $ go run -race mysrc.go // to run the source file
175 $ go build -race mycmd // to build the command
176 $ go install -race mypkg // to install the package
177
178
179 * Testing with concurrency
180
181 When testing concurrent code, there's a temptation to use sleep;
182 it's easy and works most of the time.
183
184 But "most of the time" isn't always and flaky tests result.
185
186 We can use Go's concurrency primitives to make flaky sleep-driven tests reliable.
187
188
189 * Finding errors with static analysis: vet
190
191 The `vet` tool checks code for common programmer mistakes:
192
193 - bad printf formats,
194 - bad build tags,
195 - bad range loop variable use in closures,
196 - useless assignments,
197 - unreachable code,
198 - bad use of mutexes,
199 - and more.
200
201 Usage:
202
203 go vet [package]
204
205
206 * Testing from the inside
207
208 Most tests are compiled as part of the package under test.
209
210 This means they can access unexported details, as we have already seen.
211
212
213 * Testing from the outside
214
215 Occasionally you want to run your tests from outside the package under test.
216
217 (Test files as `package` `foo_test` instead of `package` `foo`.)
218
219 This can break dependency cycles. For example:
220
221 - The `testing` package uses `fmt`.
222 - The `fmt` tests must import `testing`.
223 - So the `fmt` tests are in package `fmt_test` and can import both `testing` and `fmt`.
224
225
226 * Mocks and fakes
227
228 Go eschews mocks and fakes in favor of writing code that takes broad interfaces.
229
230 For example, if you're writing a file format parser, don't write a function like this:
231
232 func Parse(f *os.File) error
233
234 instead, write functions that take the interface you need:
235
236 func Parse(r io.Reader) error
237
238 (An `*os.File` implements `io.Reader`, as does `bytes.Buffer` or `strings.Reader`.)
239
240
241 * Subprocess tests
242
243 Sometimes you need to test the behavior of a process, not just a function.
244
245 .code testing/subprocess/subprocess.go /func Crasher/,/^}/
246
247 To test this code, we invoke the test binary itself as a subprocess:
248
249 .code testing/subprocess/subprocess_test.go /func TestCrasher/,/^}/
250
251
252 * More information
253
254 .link /pkg/testing/ go.dev/pkg/testing/
255
256 .link /cmd/go/ go.dev/cmd/go/
257
258 .link / go.dev
259
260
View as plain text