github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2014/testing.slide (about) 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 intepreted 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 http://golang.org/pkg/testing/ 255 256 .link http://golang.org/cmd/go/ 257 258 .link http://golang.org 259