Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(831)

Side by Side Diff: 2014/testing/testing.slide

Issue 108170043: code review 108170043: "Testing Techniques" talk (Closed)
Patch Set: diff -r c636a8a8316f https://code.google.com/p/go.talks Created 9 years, 9 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « 2014/testing/test2/string_test.go ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 Testing Techniques
2 Google I/O 2014
3
4 Andrew Gerrand
5 adg@golang.org
6
7
8 * The basics
9
10 * Testing Go code
11
12 Go has a built-in testing framework.
13
14 It is provided by the `testing` package and the `go` `test` command.
15
16 Here is a complete test file that tests the `strings.Index` function:
17
18 .code test1/string_test.go
19
20
21 * Table-driven tests
22
23 Go's struct literal syntax makes it easy to write table-driven tests:
24
25 .code test2/string_test.go /func TestIndex/,/^}/
26
27
28 * T
29
30 The `*testing.T` argument is used for error reporting:
31
32 t.Errorf("got bar = %v, want %v", got, want)
33 t.Fatal("Frobnicate(%v) returned error: ", arg, err)
34 t.Logf("iteration %v", i)
35
36 And enabling parallel tests:
r 2014/06/25 17:12:12 i agree with dvyukov that this is a subtle point t
37
38 t.Parallel()
39
40 And controlling whether a test runs at all:
r 2014/06/25 17:12:12 testing.Short is nice; might use it instead of Par
41
42 if runtime.GOARM == "arm" {
bcmills 2014/06/25 17:12:04 runtime.GOARCH
43 t.Skip("this doesn't work on ARM")
44 }
45
46
47 * Running tests
48
49 The `go` `test` command runs tests for the specified package.
50 (It defaults to the package in the current directory.)
51
52 $ go test
53 PASS
54
55 $ go test -v
56 === RUN TestIndex
57 --- PASS: TestIndex (0.00 seconds)
58 PASS
59
60 To run the tests for all my projects:
61
62 $ go test github.com/nf/...
63
64 Or for the standard library:
65
66 $ go test std
67
68
69 * Test coverage
70
71 The `go` tool can report test coverage statistics.
72
73 $ go test -cover
74 PASS
75 coverage: 96.4% of statements
76 ok strings 0.692s
77
78 The `go` tool can generate coverage profiles that may be intepreted by the `cove r` tool.
79
80 $ go test -coverprofile=cover.out
81 $ go tool cover -func=cover.out
82 strings/reader.go: Len 66.7%
83 strings/strings.go: TrimSuffix 100.0%
84 ... many lines omitted ...
85 strings/strings.go: Replace 100.0%
86 strings/strings.go: EqualFold 100.0%
87 total: (statements) 96.4%
88
89 * Coverage visualization
90
91 $ go tool cover -html=cover.out
92
93 .image cover.png
94
95
96 * Advanced techniques
97
98 * Testing HTTP clients and servers
99
100 The `net/http/httptest` package provides helpers for testing code that makes or serves HTTP requests.
101
102
103 * httptest.Server
104
105 An `httptest.Server` listens on a system-chosen port on the local loopback inter face, for use in end-to-end HTTP tests.
106
107 type Server struct {
108 URL string // base URL of form http://ipaddr:port with no t railing slash
109 Listener net.Listener
110
111 // TLS is the optional TLS configuration, populated with a new c onfig
112 // after TLS is started. If set on an unstarted server before St artTLS
113 // is called, existing fields are copied into the new config.
114 TLS *tls.Config
115
116 // Config may be changed after calling NewUnstartedServer and
117 // before Start or StartTLS.
118 Config *http.Server
119 }
120
121 * httptest.Server in action
122
123 This code sets up a temporary HTTP server that serves a simple "Hello" response.
124
125 .play httpserver.go /START/,/STOP/
126
127
128 * httptest.ResponseRecorder
129
130 `httptest.ResponseRecorder` is an implementation of `http.ResponseWriter` that r ecords its mutations for later inspection in tests.
131
132 type ResponseRecorder struct {
133 Code int // the HTTP response code from WriteHead er
134 HeaderMap http.Header // the HTTP response headers
135 Body *bytes.Buffer // if non-nil, the bytes.Buffer to appen d written data to
136 Flushed bool
137 }
138
139 * httptest.ResponseRecorder in action
140
141 By passing a `ResponseRecorder` into an HTTP handler we can inspect the generate d response.
142
143 .play httprecorder.go /START/,/STOP/
144
145
146 * Demo
147
148
149 * Race Detection
150
151 A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.
152
153 To help diagnose such bugs, Go includes a built-in data race detector.
154
155 Pass the `-race` flag to the go tool to enable the race detector:
156
157 $ go test -race mypkg // to test the package
158 $ go run -race mysrc.go // to run the source file
159 $ go build -race mycmd // to build the command
160 $ go install -race mypkg // to install the package
161
162
163 * Demo
164
165
166 * Testing with concurrency
167
168 When testing concurrent code, there's a temptation to use sleep;
169 it's easy and works most of the time.
170
171 But "most of the time" means flaky tests.
172
173 We can use Go's concurrency primitives to make flaky sleep-driven tests reliable .
174
175
176 * Demo
177
178
179 * Finding errors with static analysis: vet
180
181 The `vet` tool checks code for common programmer mistakes:
182
183 - bad printf strings,
r 2014/06/25 17:12:12 bad `Printf` formats
184 - bad build tags,
185 - bad range loop variable use in closures,
186 - useless assignments,
187 - unreachable code,
188 - copying locks,
189 - and more.
190
191 Usage:
192
193 go vet [package]
194
195
196 * Demo
197
198
199 * Testing from the inside
200
201 Most tests are compiled as part of the package under test.
202
203 This means they can access unexported details, as we have already seen.
204
205
206 * Testing from the outside
207
208 Occasionally you might need to run your tests from outside the package under tes t.
209
210 (Test files as `package` `foo_test` instead of `package` `foo`.)
211
212 This is done to break dependency cycles. For example:
213
214 - The `testing` package uses `fmt`.
215 - The `fmt` tests must import `testing`.
216 - So the `fmt` tests are in package `fmt_test` and import both `testing` and `fm t`.
217
218
219 * Mocks and fakes
220
221 Go eschews mocks and fakes in favor of writing code that takes broad interfaces.
222
223 For example, if you're writing a file format parser, don't write a function like this:
224
225 func Parse(f *os.File) error
226
227 instead, write functions that take the interface you need:
228
229 func Parse(r io.Reader) error
230
231 (An `*os.File` implements `io.Reader`, as does `bytes.Buffer` or `strings.Reader `.)
232
233 But if you really need to...
234
235
236 * Fakes
r 2014/06/25 17:12:12 too much about fakes in my opinion. they're very r
237
238 Got a package that works with the file system, but don't want your tests to use the disk?
239
240 The `"code.google.com/p/go.tools/godoc/vfs"` package specifies a virtual file sy stem interface:
241
242 // The FileSystem interface specifies the methods godoc is using
243 // to access the file system for which it serves documentation.
244 type FileSystem interface {
245 Opener
246 Lstat(path string) (os.FileInfo, error)
247 Stat(path string) (os.FileInfo, error)
248 ReadDir(path string) ([]os.FileInfo, error)
249 String() string
250 }
251
252 // Opener is a minimal virtual filesystem that can only open regular fil es.
253 type Opener interface {
254 Open(name string) (ReadSeekCloser, error)
255 }
256
257 * Fakes (continued)
258
259 To use it in your code, declare an unexported package global `vfs.FileSystem`:
260
261 var fs = vfs.OS("/")
262
263 and use it for all your file system acceses:
264
265 f, err := fs.Open("/path/to/file")
266
267 * Fakes (continued)
268
269 In your tests, you can swap out the `vfs.OS` file system with an alternate imple mentation, such as the in-memory `mapfs` package.
270
271 import "code.google.com/p/go.tools/godoc/vfs/mapfs"
272
273 func TestFoo(t *testing.T) {
274 // Replace fs with an in-memory file system while the test runs.
275 origfs := fs
276 defer func() { fs = origfs }() // Restore it before returning.
277 fs = mapfs.New(map[string]string{
278 "some/file/hello.txt": "Hello, test code!",
279 "another/path/bar.txt": "Donuts!",
280 })
281
282 // Test the code that uses fs.
283 }
284
285 If your code needs more of the file system than what is provided godoc's VFS imp lementation, just make a copy and implement those parts.
286
287 Use the same technique to fake out other things.
288
289
290 * Mocks: gomock
bcmills 2014/06/25 17:12:04 Gomock is overused. Could we mention when (not) t
291
292 Gomock is an external tool that, given an interface,
293
294 type MyInterface interface {
295 SomeMethod(x int64, y string)
296 }
297
298 generates a mock implementation of that interface that behaves as directed:
r 2014/06/25 17:12:12 it's not a mock implementation (although some woul
299
300 func TestMyThing(t *testing.T) {
301 mockCtrl := gomock.NewController(t)
302 defer mockCtrl.Finish()
303
304 mockObj := something.NewMockMyInterface(mockCtrl)
305 mockObj.EXPECT().SomeMethod(4, "blah")
306 // pass mockObj to a real object and play with it.
307 mockObj.SomeMethod(5, "nope") // causes test to fail
308 }
309
310
311 * Subprocess tests
312
313 Sometimes you need to test the behavior of a process, not just a function.
314
315 .code subprocess/subprocess.go /func Crasher/,/^}/
316
317 To test this code, we invoke the test binary itself as a subprocess:
318
319 .code subprocess/subprocess_test.go /func TestCrasher/,/^}/
320
321
322 * Questions?
323
OLDNEW
« no previous file with comments | « 2014/testing/test2/string_test.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b