catalogue
- 1、 Unit test classification and its concept
- 1. Basic classification
- 2. Describe the classification of unit test in detail
- 2、 Describe each test in detail in combination with the code
- 1. Benchmark test
- 2. Group test and sub test
- 3、 Pprof debugging tool
- 1. Transfer parameters to the main function
- 2. Pprof performance tuning
preface:
Usually write code according to needs Manual testing is often not comprehensive, and it is cumbersome to test because of the change of requirements
By completing a test function, you can greatly simplify the test steps, and only need to change the test input and expectation when the requirements need to change
1、 Unit test classification and its concept
1. Basic classification
- The prefix of the test function is test, which is mainly used to test whether some logical behaviors of the program are correct
- The base function name is prefixed with
Benchmark
It mainly tests the performance of the function - The prefix name of the sample function is
Example
Prompt sample documents for documents
2. Describe the classification of unit test in detail
① Test function
- Basic test of function
- Group test of function
- Subtest of function
- Test function coverage (that is, how much code of the tested function is used for execution)
- When testing, ensure that the coverage of the tested function is 100% and the coverage of the tested function is more than 60%. Otherwise, most of the written code cannot be used and needs to be optimized
- ① Test coverage can be used
go test -cover
-
②
go test -cover -coverprofile=c.out
(store the test results in the file c.out) - Then use
go tool cover -html=c.out
You can open a file to show which code has not been executed
- ① Test coverage can be used
② Benchmark test
- What does benchmark function test do: the benchmark function will define a time period for executing the code. If the code is concise, the execution times of the tested function need to be doubled (until it reaches the expectation given by the benchmark function, and then count the total number of rounds of execution and the average time of each round)
- When executing the benchmark function, first write the benchmark function
- The parameters of the benchmark function are: * testing Pointer variable corresponding to B
- A loop is carried out inside the test function, and the termination condition of the loop is b.n
2、 Describe each test in detail in combination with the code
1. Benchmark test
(1) Points needing attention of benchmark function
In the benchmark test, the algorithm of the function is often tested. Sometimes the effect of the latter algorithm will be different when the basis of the test data is different. When we need to test samples of different orders of magnitude. You can write a box as a springboard and test only the current order of magnitude of data
When testing, the command is:go test -bench=. (execute all springboard functions once) or = perform specific function test for specific function – benchtime = time (this parameter can be used to expand the time when the tested function cannot be completed within the default time of the benchmark function). When performing benchmark function test, some pre work may be required. If you feel that the pre work wastes time, you can use # B. resettimer() to reset the timer
(2) Benchmark code
The test function code is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
// File name substr_ test. go package main import ( "reflect" "testing" ) func BenchmarkSubStr(b *testing.B) { for i := 0; i < b.N; i++ { res := subStr( "qwe:qw:es:wqe" , ":" ) if !reflect.DeepEqual(res, []string{ "qwe" , "qw" , "es" , "wqe" }) { b.Errorf( "Mismatch" ) } } } func benchmarkFib(b *testing.B, n int) { for i := 0; i < b.N; i++ { Fib(n) } } func BenchmarkFib5(b *testing.B) { benchmarkFib(b, 5) } func BenchmarkFib10(b *testing.B) { benchmarkFib(b, 10) } func BenchmarkFib15(b *testing.B) { benchmarkFib(b, 15) } func BenchmarkFib20(b *testing.B) { benchmarkFib(b, 20) } |
The code of the tested function is as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
// File name substr go package main import ( "fmt" "strings" ) func subStr(str, stre string) []string { index := strings.Index(str, stre) var theSub []string for index >= 0 { // Save the data in front of the separator first temp := str[:index] // Move string back str = str[index+1:] // Retrieve subscript index = strings.Index(str, stre) if temp != "" { theSub = append(theSub, temp) } else { continue } } theSub = append(theSub, str) return theSub[:] } // Fibonacci sequence func Fib(n int) int { if n < 2 { return n } return Fib(n-1) + Fib(n-2) } func main() { fmt .Println(subStr( "q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si" , ":" )) fmt .Printf( "%#v\n" , subStr( "q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si" , ":" )) fmt .Println(123) } |
Put the above two files in the same directory and execute the test commandgo test
Test results obtained:
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
BenchmarkSubStr-8 1213681 1012 ns/op 352 B/op 14 allocs/op
PASS
ok _/ d_/ Go language learning notes / go language grammar section / go Language Advanced Grammar / 8 Unit test / benchmark 2.410s
2. Group test and sub test
- The test of a function is not just a set of test cases, so we need to write test groups to test a function. The test is generally composed of set key value pairs, and each key corresponds to a set of test data
- Individual group errors may occur during group testing, so a case can be tested separately by using subtest, which is the background of test group and subtest
The test functions are as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package main import ( "reflect" "testing" ) type testS struct { str string ste string want []string } // Conduct group test and batch test. If there is any dissatisfaction, throw an exception // func TestSubStr(t *testing.T) { // testMap := map[string]testS{ // "case_1" : { "123:eqwe:123" , ":" , []string{ "123" , "eqwe" , "123" }}, // "case_3" : { "[email protected]@:@[email protected]@:[email protected]" , "@:" , []string{ "[email protected]" , "@[email protected]" , "[email protected]" }}, // "case_4" : { "123:[email protected]:[email protected]@3" , "[email protected]" , []string{ "123:e" , "we:[email protected]@3" }}, // "case_5" : { "123:eqwe:123" , "2" , []string{ "1" , "3:eqwe:1" , "3" }}, // "case_6" : { "123:eqwe:123" , "eqwe" , []string{ "123:" , ":123" }}, // } // for k, v := range testMap { // res := subStr( v .str, v .ste) // if !reflect.DeepEqual(res, v .want) { // t.Errorf( "%v want:%#v got:%#v" , k, v .want, res) // } // } // } // Sub test can be conducted for a sub sample func TestSubStr(t *testing.T) { testMap := map[string]testS{ "case_1" : { "13:eqwe:123" , ":" , []string{ "123" , "eqwe" , "123" }}, "case_3" : { "[email protected]@:@[email protected]@:[email protected]" , "@:" , []string{ "[email protected]" , "@[email protected]" , "[email protected]" }}, "case_4" : { "123:[email protected]:[email protected]@3" , "[email protected]" , []string{ "123:e" , "we:[email protected]@3" }}, "case_5" : { "23:eqwe:123" , "2" , []string{ "1" , "3:eqwe:1" , "3" }}, "case_6" : { "123:eqwe:123" , "eqwe" , []string{ "123:" , ":123" }}, } for k, v := range testMap { t.Run(k, func(t *testing.T) { res := subStr( v .str, v .ste) if !reflect.DeepEqual(res, v .want) { t.Errorf( "want:%#v got:%#v" , v .want, res) } }) } } |
The functions to be tested are as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package main import ( "fmt" "strings" ) func subStr(str, stre string) []string { index := strings.Index(str, stre) var theSub []string for index >= 0 { // Save the data in front of the separator first temp := str[:index] // Move string back str = str[index+len(stre):] // Retrieve subscript index = strings.Index(str, stre) if temp != "" { theSub = append(theSub, temp) } else { continue } } theSub = append(theSub, str) return theSub[:] } func main() { fmt .Println(subStr( "q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si" , ":c" )) fmt .Printf( "%#v\n" , subStr( "q:w:ec:wer:cd:scn:cj:c:is:icc:cin:si" , ":" )) fmt .Println(123) } |
(1). Group test result analysis and command:
- The group test command is still used
go test
— FAIL: TestSubStr (0.00s)
— FAIL: TestSubStr/case_1 (0.00s)
subStr_test.go:46: want:[]string{“123”, “eqwe”, “123”} got:[]string{“13”, “eqwe”, “123”}
— FAIL: TestSubStr/case_2 (0.00s)
subStr_test.go:46: want:[]string{“1”, “eqwe”, “[email protected]”} got:[]string{“eqwe”, “[email protected]”}
— FAIL: TestSubStr/case_3 (0.00s)
subStr_test.go:46: want:[]string{“[email protected]”, “@[email protected]”, “[email protected]”} got:[]string{“[email protected]”, “@[email protected]”, “[email protected]”}
— FAIL: TestSubStr/case_5 (0.00s)
subStr_test.go:46: want:[]string{“1”, “3:eqwe:1”, “3”} got:[]string{“3:eqwe:1”, “3”}
FAIL
exit status 1
FAIL _/ D_/ Go language learning notes / go language grammar section / go Language Advanced Grammar / 8 Unit test / group test and sub test 0.155s
(2). Sub test result analysis and command:
- For example, test case separately_ 1. The command used is
go test -v -run=TestSubStr/case_1
(the equal sign is followed by the directory after the test failure case fail of the above group)
=== RUN TestSubStr
=== RUN TestSubStr/case_1
subStr_test.go:46: want:[]string{“123”, “eqwe”, “123”} got:[]string{“13”, “eqwe”, “123”}
— FAIL: TestSubStr (0.00s)
— FAIL: TestSubStr/case_1 (0.00s)
FAIL
exit status 1
FAIL _/ D_/ Go language learning notes / go language grammar section / go Language Advanced Grammar / 8 Unit test / group test and sub test 0.186s
3、 Pprof debugging tool
1. Transfer parameters to the main function
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
(1)os.Args package main1 import ( "fmt" "os" ) func main() { // os. Args can pass parameters when executing the function, but there is no way to parse - name = XXX if os.Args != nil { for index, temp := range os.Args { fmt .Println( "Article" , index+1, The first argument is: , temp) } } fmt .Println( "hello" ) } |
(2)flag.Args
- Relative to OS For args, flag Args is more convenient to use
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package main import ( "flag" "fmt" "time" ) func main() { // The first parameter is the corresponding parameter, the second parameter is the default value, and the third parameter is prompt // Returns a pointer of the corresponding type // name := flag.String( "name" , "Tom" , "Enter name" ) // sex := flag.Bool( "sex" , true , "Is it male?" ) // age := flag.Int( "age" , 10, "Age" ) // flag.Parse() // fmt .Println(*name, *sex, *age) var name string var age int var sex bool flag.StringVar(&name, "name" , "Tom" , "Enter name" ) flag.BoolVar(&sex, "sex" , true , "Is it male?" ) flag.IntVar(&age, "age" , 10, "Age" ) tim := flag.Duration( "time" , time .Hour, "Time" ) // Parse the input data. If you don't use this sentence, you can't get the value corresponding to attributes such as name, sex, age, etc flag.Parse() fmt .Println(name, sex, age, *tim) fmt .Println(flag.Args()) // Returns parameters other than the command line in a sliced manner fmt .Println(flag.NArg()) // Returns the number of parameters other than the command line fmt .Println(flag.NFlag()) // Returns the number of parameters using the command line } |
2. Pprof performance tuning
pprof
The debugging tool mainly focuses on the time efficiency of the test module. When debugging, only code segments that consume more time and space will be displayed
Files that generate debug code blocks:go run xx.exe -cpu....
Use go language tools to check the problems of code blocks:go tool pprof cpu.pprof
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package main import ( "flag" "fmt" "os" "runtime/pprof" "time" ) // A piece of problematic code func logicCode() { var c chan int for { select { case v := <-c: fmt .Printf( "recv from chan, value:%v\n" , v ) default: } } } func main() { var isCPUPprof bool var isMemPprof bool // Get received parameters flag.BoolVar(&isCPUPprof, "cpu" , false , "turn cpu pprof on" ) flag.BoolVar(&isMemPprof, "mem" , false , "turn mem pprof on" ) flag.Parse() // Determine what test to do, then branch and save the information to the corresponding file. if isCPUPprof { file , err := os.Create( "./cpu.pprof" ) if err != nil { fmt .Printf( "create cpu pprof failed, err:%v\n" , err) return } pprof.StartCPUProfile( file ) defer pprof.StopCPUProfile() } for i := 0; i < 8; i++ { go logicCode() } time .Sleep(20 * time .Second) if isMemPprof { file , err := os.Create( "./mem.pprof" ) if err != nil { fmt .Printf( "create mem pprof failed, err:%v\n" , err) return } pprof.WriteHeapProfile( file ) file .Close() } } |
Summary:
Here, the commonly used test functions and sub tests are explained in detail, and the parameter tuning of pprof is only introduced. You can find some examples of pprof on the Internet according to your interest, and it is very convenient to carry out automatic testing.
This is the end of this article about the super detailed analysis of go language unit test. For more information about go language unit test, please search the previous articles of developeppaper or continue to browse the relevant articles below. I hope you will support developeppaper in the future!