This is a creation in Article, where the information may have evolved or changed.
This article is from the Golang core Development group Andrew Gerrand in the Google I/O 2014 theme Sharing "Testing techniques", that is, the use of Golang development will use the test technology (mainly for Unit test ), including the basic technology, advanced technology (concurrency test, mock/fake, competitive conditions test, concurrency test, internal/external testing, vet tools, etc.), and so on, the feeling summed up very comprehensive, here to record down, hope to bring you help. The original slide access needs to take his own ladder. Also here to spit Groove: Golang official station of the slide are in a unique Golang artical format released (with this tool http://go-talks.appspot.com/can be viewed online), can't download like a PDF, Domestic use and transmission are extremely inconvenient.
First, the basic testing technology
1. Test Go Code
Go language built-in test framework.
The built-in test framework provides test functionality through the testing package and go test commands.
The following is a complete test strings. Complete test file for the index function:
Strings_test.go (This sample code is put into the Strings_test.go file)
Package Strings_test
Import (
"Strings"
"Testing"
)
Func testindex (t *testing. T) {
const S, sep, want = "chicken", "Ken", 4
Got: = Strings. Index (S, Sep)
If got! = want {
T.errorf ("Index (%q,%q) =%v; Want%v ", S, Sep, got, want)//note the slide and got in the original want write the reverse
}
}
$go test-v Strings_test.go
= = = RUN Testindex
-pass:testindex (0.00 seconds)
PASS
OK command-line-arguments 0.007s
The-v option of Go test represents the verbose execution information for the output.
To modify the want constant value in code to 3, we create a test that cannot be passed:
$go test-v Strings_test.go
= = = RUN Testindex
-fail:testindex (0.00 seconds)
Strings_test.go:12:index ("Chicken", "ken") = 4; Want 3
FAIL
Exit Status 1
FAIL command-line-arguments 0.008s
2. Table-Driven test
Golang's struct literal (struct literals) syntax allows us to easily write table-driven tests.
Package Strings_test
Import (
"Strings"
"Testing"
)
Func testindex (t *testing. T) {
var tests = []struct {
s string
Sep string
out int
}{
{"", "", 0},
{"", "a",-1},
{"Fo", "foo",-1},
{"Foo", "foo", 0},
{"Oofofoofooo", "F", 2},
etc
}
For _, Test: = Range Tests {
Actual: = Strings. Index (Test.s, TEST.SEP)
If actual! = Test.out {
T.errorf ("Index (%q,%q) =%v; Want%v ",
Test.s, Test.sep, Actual, test.out)
}
}
}
$go test-v Strings_test.go
= = = RUN Testindex
-pass:testindex (0.00 seconds)
PASS
OK command-line-arguments 0.007s
3. T structure
*testing. The T parameter is used for error reporting:
T.errorf ("Got bar =%v, want%v", got, Want)
T.fatalf ("Frobnicate (%V) returned error:%v", ARG, err)
T.LOGF ("Iteration%v", i)
can also be used for enable parallel testing (Parallet test):
T.parallel ()
Controls whether a Test runs:
If runtime. Goarch = = "Arm" {
T.skip ("This doesn ' t work on ARM")
}
4. Run the test
We use the Go Test command to run a test for a specific package.
The default is to execute the test code for the package under the current path.
$ go Test
PASS
$ go test-v
= = = RUN Testindex
-pass:testindex (0.00 seconds)
PASS
To run all the tests under the project, we execute the following command:
$ go Test github.com/nf/...
Test of the standard library:
$ GO Test STD
Note: Assuming that the current directory of Strings_test.go is Testgo, executing go test in the Testgo directory is OK. But what happens if we switch to the top-level directory of Testgo to execute go test?
$go Test Testgo
Can ' t load package:package testgo:cannot Find package "Testgo" in any of:
/usr/local/go/src/pkg/testgo (from $GOROOT)
/users/tony/test/gotoolsprojects/src/testgo (from $GOPATH)
Testgo This package is not found, the go test should be followed by a package name, and go test will find the package under Goroot and Gopath and execute the package tests.
5. Test coverage
The Go Tool command can report test coverage statistics.
We executed go test-cover under Testgo and the results were as follows:
Go build _/users/tony/test/go/testgo:no buildable go source files In/users/tony/test/go/testgo
FAIL _/users/tony/test/go/testgo [Build failed]
It is obvious that testing coverage by cover parameter options requires not only testing the code, but also the source file of the object being measured (usually the function).
We switch the directory to $goroot/src/pkg/strings and execute go test-cover:
$go test-v-cover
= = = RUN Testreader
-pass:testreader (0.00 seconds)
... ...
= = = Run:exampletrimprefix
-pass:exampletrimprefix (1.75us)
PASS
coverage:96.9% of statements
OK strings 0.612s
Go test can generate a profile of coverage, which can be parsed by the Go tool cover tools.
Execute under $goroot/src/pkg/strings:
$ go test-coverprofile=cover.out
The Cover.out file is then generated in the current directory.
There are two ways to view the Cover.out file:
A) cover-func=cover.out
$sudo Go Tool cover-func=cover.out
Strings/reader.go:24:len 66.7%
Strings/reader.go:31:read 100%
Strings/reader.go:44:readat 100%
Strings/reader.go:59:readbyte 100%
Strings/reader.go:69:unreadbyte 100%
... ...
Strings/strings.go:638:replace 100%
Strings/strings.go:674:equalfold 100%
Total: (statements) 96.9%
b) Visual view
Execute the Go tool cover-html=cover.out command to generate a directory coverxxxxxxx, such as/tmp/cover404256298, in the/tmp directory. There is a coverage.html file under the directory. Open coverage.html with a browser, which allows you to visualize the test coverage of your code.
On the Go tool's cover command, my go version go1.3 Darwin/amd64 is not brought by default and needs to be downloaded via go get.
$sudo gopath=/users/tony/test/gotoolsprojects Go get code.google.com/p/go.tools/cmd/cover
After downloading, cover is installed under $GOROOT/PKG/TOOL/DARWIN_AMD64.
Second, advanced testing technology
1. An example program
Outyet is a Web service that declares whether a particular go version has been tagged for publication. How to get it:
Go get Github.com/golang/example/outyet
Note:
After the go get executes, the CD $GOPATH/src/github.com/golang/example/outyet and executes go run main.go. The Web service can then be accessed by opening http://localhost:8080 in a browser.
2. Test HTTP client and server
The Net/http/httptest package provides a number of helper functions for testing code that sends or processes HTTP requests.
3, Httptest. Server
Httptest. Server listen on a system-selected port on the local loopback gateway. It is often used for end-to-end HTTP testing.
Type Server struct {
URL string//base URL of form Http://ipaddr:port with no trailing slash
Listener Net. Listener
TLS is the optional TLS configuration, populated with a new config
After TLS is started. If set on an unstarted server before StartTLS
is called, existing fields was copied into the new config.
TLS *tls. Config
Config May is changed after calling Newunstartedserver and
Before Start or StartTLS.
Config *http. Server
}
Func NewServer (Handler http. Handler) *server
Func (*server) Close () error
4, Httptest. Server combat
The following code creates a temporary HTTP Server that returns a simple Hello answer:
TS: = httptest. NewServer (http. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {
Fmt. Fprintln (W, "Hello, client")
}))
Defer TS. Close ()
Res, ERR: = http. Get (TS. URL)
If err! = Nil {
Log. Fatal (ERR)
}
Greeting, err: = Ioutil. ReadAll (Res. Body)
Res. Body.close ()
If err! = Nil {
Log. Fatal (ERR)
}
Fmt. Printf ("%s", greeting)
5, Httptest. Responserecorder
Httptest. Responserecorder is an implementation of Http.responsewriter, used to record changes and used in subsequent inspections of tests.
Type Responserecorder struct {
code INT//The HTTP response code from Writeheader
Headermap http. Header//The HTTP response headers
Body *bytes. Buffer//If Non-nil, the bytes. Buffer to append written data to
flushed bool
}
6, Httptest. Responserecorder Combat
A responserecorder is passed into an HTTP handler, through which we can view the generated response.
Handler: = Func (w http. Responsewriter, R *http. Request) {
http. Error (W, "Something Failed", http. Statusinternalservererror)
}
Req, Err: = http. Newrequest ("GET", "Http://example.com/foo", nil)
If err! = Nil {
Log. Fatal (ERR)
}
W: = Httptest. Newrecorder ()
Handler (W, req)
Fmt. Printf ("%d–%s", W.code, W.body.string ())
7. Competition detection (race detection)
Data contention occurs when two goroutine concurrently access the same variable, and at least one goroutine writes to the variable.
To assist in diagnosing this bug,go provides a built-in data competition detection tool.
With the incoming-race option, the Go tool can start competitive detection.
$ go test-race mypkg//To test the package
$ go run-race mysrc.go//To run the source file
$ go build-race mycmd//To build the command
$ go install-race mypkg//To install the package
Note: An example of a data competition test
Example code:
Testrace.go
Package Main
Import "FMT"
Import "Time"
Func Main () {
var i int = 0
Go func () {
for {
i++
Fmt. Println ("subroutine:i =", i)
Time. Sleep (1 * time. Second)
}
}()
for {
i++
Fmt. Println ("mainroutine:i =", i)
Time. Sleep (1 * time. Second)
}
}
$go Run-race Testrace.go
MAINROUTINE:I = 1
==================
Warning:data RACE
Read by Goroutine 5:
Main.func 001 ()
/users/tony/test/go/testrace.go:10 +0x49
Previous Write by main goroutine:
Main.main ()
/users/tony/test/go/testrace.go:17 +0xd5
Goroutine 5 (running) created at:
Main.main ()
/users/tony/test/go/testrace.go:14 +0xaf
==================
SUBROUTINE:I = 2
MAINROUTINE:I = 3
SUBROUTINE:I = 4
MAINROUTINE:I = 5
SUBROUTINE:I = 6
MAINROUTINE:I = 7
SUBROUTINE:I = 8
8. Test concurrency (testing with concurrency)
When testing concurrent code, there is always an impulse to use sleep. For most of the time, using sleep is both simple and effective.
But most of the time is not "always".
We can use the concurrency primitives of go to make the tests that are strange and unreliable sleep drivers more trustworthy.
9. finding errors using the static analysis tool vet
The Vet tool is used to detect common mistakes programmers make in code:
– The wrong printf format
– Wrong build tag
– Use the wrong range loop variable in the closure
– Useless assignment operation
– Code that cannot be reached
– Incorrect use of mutexes
Wait a minute.
How to use:
Go vet [Package]
10. from internal testing
Most of the test code in Golang is part of the source of the package being tested. This means that the test code can access symbols that are not exported by the package and internal logic. Just like we saw before.
Note: $goroot/src/pkg/path/path_test.go and path.go, for example, are under the package path.
11, from the external testing
In some cases, you need to test the package from the outside of the package, such as the test code under the packages Foo_test, rather than the packages foo.
This can break the dependency cycle, such as:
–testing Package Using FMT
The test code for the –FMT package must also be imported into the testing package
– The test code for the FMT package is then placed under the Fmt_test package so that both the testing package can be imported or the FMT package can be imported at the same time.
12, mocks and fakes
You can avoid using mock and fake test mechanisms by using Interface,go in your code.
For example, if you are writing a file format parser, do not design functions like this:
Func Parser (f *os. File) Error
Instead, you can write a function that accepts the interface type:
Func Parser (R io. Reader) Error
and bytes. Buffer, Strings. Like reader, *os. File also implements the Io.reader interface.
13. Sub-process Testing
There are times when you need to test the behavior of a process, not just a function. For example:
Func crasher () {
Fmt. Println ("Going in flames!")
Os. Exit (1)
}
To test the code above, we tested the test program itself as a child process:
Func testcrasher (t *testing. T) {
If OS. Getenv ("be_crasher") = = "1" {
Crasher ()
Return
}
CMD: = Exec.command (OS. Args[0], "-test.run=testcrasher")
Cmd. ENV = append (os. Environ (), "be_crasher=1")
ERR: = cmd. Run ()
If e, OK: = Err. (*exec. EXITERROR); OK &&!e.success () {
Return
}
T.fatalf ("process ran with err%v, want exit status 1", err)
}
, Bigwhite. All rights reserved.