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