This is the 8th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021.
Why write unit tests
- Reduce bugs
- Improve code quality
- Rest assured refactoring
1 Introduction to basic rules
Unit testing for Go is easier to implement because the Go language provides a framework for unit testing. A framework for unit testing should follow the following rules.
- 1. The go file for unit test code must be in the
_test.go
At the end, the Go language testing tool only recognizes files that meet this rule - 2. The function name of the unit test must be
Test
At the beginning, there are functions that can be exported to the public. Note: The best function name is Test+ the name of the method function to be tested - 3. The signature of the test function must receive a pointer
A pointer to type testing.t
As an argument, and the test function cannot return any value
2 Go Test tool
The go test command is a driver that tests code by convention and organization. In the package directory, all source files with the suffix _test.go are part of the Go test test and will not be compiled into the final executable by the Go build.
There are three types of functions in the *_test.go file, unit test functions, benchmark functions, and sample functions. The go test command iterates through all the named functions in the *_test.go file, generates a temporary main package that calls the corresponding test function, builds and runs it, reports the test results, and finally cleans up the temporary files generated during the test.
type | format | role |
---|---|---|
Test functions | The function name is prefixed with Test | Test whether some logical behavior of the program is correct |
Benchmark functions | The function name is prefixed with Benchmark | Test the performance of the function |
The sample function | The function name is prefixed with Example | Provide sample documentation for documentation |
## 3 Test the function | ||
### 3.1 Test function format | ||
Each test file function must start with_test.go End and importtesting Package, the basic format of the test function (signature) is as follows: |
||
“`go | ||
func TestName(t *testing.T){ |
// Test the code bodyCopy the code
}
```go
func TestAdd(t *testing.T){ ... }
func TestSum(t *testing.T){ ... }
func TestLog(t *testing.T){ ... }
Copy the code
The parameter t is used to report test failures and additional log information. Testing.t has the following methods:
func (c *T) Error(args ...interface{})
func (c *T) Errorf(format string, args ...interface{})
func (c *T) Fail(a)
func (c *T) FailNow(a)
func (c *T) Failed(a) bool
func (c *T) Fatal(args ...interface{})
func (c *T) Fatalf(format string, args ...interface{})
func (c *T) Log(args ...interface{})
func (c *T) Logf(format string, args ...interface{})
func (c *T) Name(a) string
func (t *T) Parallel(a)
func (t *T) Run(name string, f func(t *T)) bool
func (c *T) Skip(args ...interface{})
func (c *T) SkipNow(a)
func (c *T) Skipf(format string, args ...interface{})
func (c *T) Skipped(a) bool
Copy the code
3.2 Test function Example 1
package junit
import (
"fmt"
"testing"
)
func sum(x,y int) int {
return x+y
}
func TestSum(t *testing.T) {
sum := sum(10.20)
expected:=30
ifsum! = expected { t.Errorf("Expected execution %d, but got %d!", expected sum)} FMT. Println (sum)} -- -- -- -- -- -- -- -- -- -- -- -- the execution result -- -- -- -- -- -- -- -- -- -- - = = = RUN TestSum30
--- PASS: TestSum (0.00s)
PASS
Copy the code
3.3 Test function Example 2
package test
import (
"fmt"
"testing"
)
/ / add
func Add1(a, b int) int {
return a + b
}
func Add2(a, b int) int {
return a + b
}
func Add3(a, b int) int {
return a + b
}
// 1. Unit test initializes execution
func TestMain(m *testing.M) {
fmt.Println("Initialize here...")
m.Run()
}
// 2. Customize the order in which unit tests are executed
func TestAll(t *testing.T) {
t.Run("TestAdd2", testAdd2)
t.Run("TestAdd1", testAdd1)
t.Run("TestAdd3", testAdd3)
}
func testAdd1(t *testing.T) {
i := Add1(5.6)
ifi ! =11 {
t.Errorf("Failed unit test TestAdd1")}}func testAdd2(t *testing.T) {
i := Add2(5.6)
ifi ! =11 {
t.Errorf("Failed unit test TestAdd2")}}// 3. Skip the unit test with t.skipnow ()
func testAdd3(t *testing.T) {
t.SkipNow()
i := Add3(5.6)
ifi ! =10 {
t.Errorf("Failed unit test TestAdd3")}}Copy the code
4 Benchmarks
- 1.File naming rules:
The GO file containing the unit test code must be in the_test.go
At the end, the Go language testing tool only recognizes files that meet this rule
Unit test file name_test.go
Preferably, the preceding section should be the name of the go file in which the method being tested resides. - 2,Function declaration rules:The signature of the test function must receive a pointer
testing.B
Type, and the function returns no value. - 3,Function naming rules:
The function name of the unit test must beBenchmark
At the beginning, it’s a function that can be exported to the public, preferablyBenchmark+
The name of the method function to test. - 4,Function body design rules:
b.N
Is provided by the benchmark framework to control the number of loops that loop through the test code to evaluate performance.b.ResetTimer()/b.StartTimer()/b.StopTimer()
Used to control timers to accurately control the time spent on performance testing code.
4.1 Benchmark function format
Benchmarking is a way to test application performance under a certain workload. The basic format of the benchmark is as follows:
func BenchmarkName(b *testing.B){
// ...
}
Copy the code
A Benchmark test is prefixed with a *testing.B parameter B. The Benchmark test must be executed for b.N times, so that the test can be compared. Testing.B has the following methods:
func (c *B) Error(args ...interface{})
func (c *B) Errorf(format string, args ...interface{})
func (c *B) Fail(a)
func (c *B) FailNow(a)
func (c *B) Failed(a) bool
func (c *B) Fatal(args ...interface{})
func (c *B) Fatalf(format string, args ...interface{})
func (c *B) Log(args ...interface{})
func (c *B) Logf(format string, args ...interface{})
func (c *B) Name(a) string
func (b *B) ReportAllocs(a)
func (b *B) ResetTimer(a)
func (b *B) Run(name string, f func(b *B)) bool
func (b *B) RunParallel(body func(*PB))
func (b *B) SetBytes(n int64)
func (b *B) SetParallelism(p int)
func (c *B) Skip(args ...interface{})
func (c *B) SkipNow(a)
func (c *B) Skipf(format string, args ...interface{})
func (c *B) Skipped(a) bool
func (b *B) StartTimer(a)
func (b *B) StopTimer(a)
Copy the code
4.2 Sample benchmark tests
The benchmark is not executed by default, so we need to add the -bench parameter, so we run the benchmark by executing the go test-bench =Sum command.
Use go test-bench =Sum -benchmem for each operation
package junit
import (
"testing"
)
func sum(x,y int) int {
return x+y
}
func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
sum(i, i+2)}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- goos: Windows goarch: amd64 PKG: projectDemo1 / junit BenchmarkSum4 - 1000000000 0.606 ns/op
PASS
// Here 1000000000 refers to the number of iterations, which is determined by Go itself,
// 0.606ns/op refers to 0.606 nanoseconds each time Sum is executed.
Copy the code
4.3 Performance Comparison function
Benchmarking can only get the absolute elapsed time of a given operation, but many performance problems are the relative elapsed time between two different operations, and we often need to benchmark the implementation of two different algorithms with the same input. A performance comparison function is usually a function that takes parameters and is called by different Benchmark functions passing in different values. Here’s an example:
func benchmark(b *testing.B, size int){/ *... * /}
func Benchmark10(b *testing.B){ benchmark(b, 10)}func Benchmark100(b *testing.B){ benchmark(b, 100)}func Benchmark1000(b *testing.B){ benchmark(b, 1000)}Copy the code
4.4 Performance comparison test
package junit
import "testing"
// Fib is a function that computes the NTH Fibonacci number
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n- 1) + Fib(n2 -)}// performance comparison function written by benchmarkFib
func benchmarkFib(b *testing.B, n int) {
for i := 0; i < b.N; i++ {
Fib(n)
}
}
func BenchmarkFib1(b *testing.B) { benchmarkFib(b, 1)}func BenchmarkFib2(b *testing.B) { benchmarkFib(b, 2)}func BenchmarkFib3(b *testing.B) { benchmarkFib(b, 3)}func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10)}func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20)}func BenchmarkFib40(b *testing.B) { benchmarkFib(b, 40)}Copy the code
Run go test-bench =. Under the current file to benchmark the result
goos: windows
goarch: amd64
pkg: projectDemo1/junit
BenchmarkFib14 - 267679506 3.99 ns/op
BenchmarkFib24 - 125047687 9.38 ns/op
BenchmarkFib34 - 52140361 20.8 ns/op
BenchmarkFib104 - 2096516 559 ns/op
BenchmarkFib204 - 14952 70338 ns/op
BenchmarkFib404 - 1 1076715400 ns/op
PASS
ok projectDemo1/junit 10.189s
Copy the code
4.5 Reset Time
B. Resettimer processing before will not be put into the execution time, and will not be output to the report, so you can do some operations not planned as a test report before. Such as:
package junit
import (
"testing"
"time"
)
func sum(x,y int) int {
return x+y
}
func BenchmarkSum(b *testing.B) {
time.Sleep(5 * time.Second) // Suppose you need to do some time-consuming, unrelated operations
b.ResetTimer() // Reset the timer
for i := 0; i < b.N; i++ {
sum(i,i+2)}} -- -- -- -- -- -- -- -- -- -- -- -- the output -- -- -- -- -- -- -- -- -- -- -- -- goos: Windows goarch: amd64 PKG: projectDemo1 / junit BenchmarkSum4 - 1000000000 0.449 ns/op
PASS
Copy the code
4.6 Parallel Testing
Executes a given benchmark in parallel. RunParallel creates multiple Goroutines and assigns b.N to them for execution, where the default number of Goroutines is GOMAXPROCS.
Users who want to increase parallelism for non-CPU-bound benchmarks can call SetParallelism before RunParallel. RunParallel is usually used with the -CPU flag
package junit
import (
"testing"
)
func sum(x,y int) int {
return x+y
}
func BenchmarkSumParallel(b *testing.B) {
// b.setParallelism (1) // Set the number of cpus used
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
sum(12.35)}}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the output -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- goos: Windows goarch: amd64 PKG: projectDemo1/junit BenchmarkSumParallel4 - 1000000000 1.04 ns/op
PASS
Copy the code
5 Example Functions
5.1 Format of the example function
The third type of function treated specifically by Go Test is the Example function, which is prefixed with Example. They have neither arguments nor return values. The standard format is as follows:
func ExampleName(a) {
// ...
}
Copy the code
5.2 Examples Examples of functions
func ExampleSum(a) {
fmt.Println(sum(10.31))
fmt.Println(sum(123.233))
// Output:
/ / [41]
/ / [356]
}
Copy the code
5.3 Sample code usage
- Sample functions can be used directly as documentation, such as the Web-based GODoc that associates sample functions with corresponding functions or packages.
- The sample function, as long as it contains // Output: is also an executable test that can be run with go test.
- Example function provides the example code that can be run directly, can be directly ingolang.orgthe
godoc
Used on the document serverGo Playground
Run the sample code. The following is a sample function effect of the strings.ToUpper function on Playground.
6 More References
Please refer to the Chinese documentation of the Go standard Library