The important point of the development program is the test, how we guarantee the quality of the code, how to ensure that each function is operational, the result is correct, and how to ensure that the written code performance is good, we know that the focus of unit testing is to find the design or implementation of logic errors, so that the problem early exposure, Easy to solve the problem, and the focus of performance testing is to find some programming problems, so that the online program can be high concurrency in the case can remain stable. This section will take this series of questions to explain how to implement unit and performance testing in the Go language.
The go language comes with a lightweight test framework testing and its own go test commands for unit testing and performance testing, and the testing framework is similar to a test framework in other languages, where you can write test cases for corresponding functions based on this framework. You can also write the appropriate stress test cases based on the framework, so let's take a look at how to write hit. How to write test cases
Since the Go Test command can only execute all the files in one of the corresponding directories, we will then create a new project directory Gotest so that all our code and test code are in this directory.
Next we create two files under this directory: Gotest.go and Gotest_test.go
Gotest.go: In this file we create a package with a function that divides the operation:
Package gotest
Import (
"errors"
)
func Division (A, B float64) (float64, error) {
if b = = 0 {
Retu RN 0, errors. New ("Divisor cannot be 0")
}
return a/b, nil
}
Gotest_test.go: This is our unit test file, but keep in mind the following principles: The file name must be _test.go end, so that when the go test is executed to the corresponding code you must import testing this package All of the test case functions must be test-first tests are executed in the order in which they were written in the source code. The parameter of the test function testxxx () is testing. T, we can use this type to log errors or test the status Test format: Func testxxx (t *testing. T), the xxx section can be any combination of alphanumeric, but the first letter cannot be a lowercase letter [a-z], for example testintdiv is the wrong function name. function, by calling testing. T's error, Errorf, Failnow, Fatal, Fatalif method, indicates that the test does not pass, and calls the log method to log the information of the test.
Here is the code for our test case:
Package gotest
Import (
"testing"
)
func test_division_1 (t *testing. T) {
If I, E: = Division (6, 2); I! = 3 | | E! = NIL {//try A unit test on function
t.error ("Division function test Failed")//if not as expected The error
} else {
t.Log ("first Test Passed")//record some information you expect to record
}
}
func test_division_2 (t *testing. T) {
t.error ("Just don't Pass")
}
We execute go test below the project directory and the following information is displayed:
---fail:test_division_2 (0.00 seconds)
Gotest_test.go:16: does not pass
fail
exit status 1
fail gotest 0.013s
From this result it is shown that the test did not pass, because in the second Test function we wrote dead the code T passed by the test. Error, what happens to our first function execution? By default, go test does not display the information passed by the tests, we need to bring the parameter Go test-v, which will display the following information:
= = = Run Test_division_1
---pass:test_division_1 (0.00 seconds)
Gotest_test.go:11: The first test passed the
= = = Run Test_ Division_2
---fail:test_division_2 (0.00 seconds)
Gotest_test.go:16: does not pass
fail
exit status 1
FAIL gotest 0.012s
The above output shows the process of this test in detail, we see the test function 1test_division_1 test pass, and the test function 2test_division_2 test failed, and finally concluded that the test does not pass. Next we change the test function 2 to the following code:
Func test_division_2 (t *testing. T) {
If _, E: = Division (6, 0), E = = Nil {//try A unit test on function
t.error ("Division do not work as Expecte D. ") If not as expected then error
} else {
t.Log ("one Test passed.", e)//record some information you wish to record
}
}
Then we execute Go test-v, the following information is displayed, the test passed:
= = = Run Test_division_1
---pass:test_division_1 (0.00 seconds)
Gotest_test.go:11: The first test passed the
= = = Run Test_ Division_2
---pass:test_division_2 (0.00 seconds)
Gotest_test.go:20:one Test passed. Divisor cannot be 0
PASS
OK gotest 0.013s
How to write a stress test
Stress tests are used to detect the performance of functions (methods), similar to the method of writing unit functional tests, which are not mentioned here, but note the following points:
The stress test case must follow the following format, where xxx can be a combination of any alphanumeric, but the first letter cannot be a lowercase letter
Func benchmarkxxx (b *testing. B) {...}
Go test does not perform the stress test function by default, if you want to perform a stress test with parameter-test.bench, syntax:-test.bench= "Test_name_regex", such as Go test-test.bench= ". *" Means testing all the stress test functions in the stress test case, remember to use testing in the loop body. B.N to allow the test to run correctly the file name must also end with _test.go
Here we create a new stress test file, Webbench_test.go, as shown below:
Package gotest
Import (
"testing"
)
func benchmark_division (b *testing. B) {
for I: = 0; i < B.N; i++ {//use B.N for looping
Division (4, 5)
}
}
func Benchmark_timeconsumi Ngfunction (b *testing. b) {
b.stoptimer ()//Call this function to stop the time count of the stress test
//Do some initialization work, such as reading the file data, database connection and so on,
//so that these times do not affect the performance of our test function
b . Starttimer ()//Restart time for
I: = 0; i < B.N; i++ {
Division (4, 5)}
}
We execute the command go test-test.bench= ". *", you can see the following results:
PASS
benchmark_division 500000000 7.76 ns/op
benchmark_timeconsumingfunction 500000000 7.80 Ns/op
OK gotest 9.364s
The above results show that we did not perform any TESTXXX unit test functions, the results show only the pressure test function, the first shows that the benchmark_division executed 500 million times, The average execution time for each time is 7.76 nanoseconds, and the second shows that the benchmark_timeconsumingfunction executes 500000000, and the average execution time for each time is 7.80 nanoseconds. The last bar shows the total execution time.
We execute the command go test-test.bench= ". *"-count=5, you can see the following results: (using-count can specify how many times to execute)
PASS
benchmark_division-2 300000000 4.60 ns/op
benchmark_division-2 300000000 4.57 ns/op
benchmark_division-2 300000000 4.63 ns/op
benchmark_division-2 300000000 4.60 ns/op
benchmark_division-2 300000000 4.63 ns/op
benchmark_timeconsumingfunction-2 300000000 4.64 ns/op
Benchmark_timeconsumingfunction-2 300000000 4.61 ns/op
benchmark_timeconsumingfunction-2 300000000 4.60 ns/op
benchmark_timeconsumingfunction-2 300000000 4.59 ns/op
Benchmark_ TimeConsumingFunction-2 300000000 4.60 ns/op
OK _/home/diego/gowork/src/app/testing 18.546s
Go test-run= file name-bench=bench name-cpuprofile= production cprofile file name folder
Example:
There is a popcnt folder under Testbenchmark, and there are files in popcnt popcunt_test.go
➜ testbenchmark ls
popcnt
Popcunt_test.go's Asking price content:
Ackage popcnt
Import (
"testing"
)
Const M1 = 0x5555555555555555
const m2 = 0x3333333333333333
Const M4 = 0x0f0f0f0f0f0f0f0f
Const H01 = 0x0101010101010101
func popcnt (x UInt64) UInt64 {
X-= (x >& Gt 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & M4
return (x * h01) >>
benchmarkpopcnt}
func (b *testing. B) {for
I: = 0; i < B.N; i++ {
x: = i
X-= (x >> 1) & m1
x = (x & m2) + ((x >> 2) & m2)
x = (x + (x >> 4)) & M4
_ = (x * h01) >> (+)}
}
Then run go test-bench= ". *"-cpuprofile=cpu.profile./popcnt
➜ Testbenchmark Go test-bench= ". *"-cpuprofile=cpu.profile./popcnt testing:warning:no
tests to run
pass< C3/>benchmarkpopcnt-8 1000000000 2.01 ns/op
OK app/testbenchmark/popcnt 2.219s
➜ Testbenchmark ll Total
6704
drwxr-xr-x 5 Diego 5 6 13:57.
Drwxr-xr-x 3 Diego staff 102 5 6 11:12.
-rw-r--r-- 1 Diego staff 5200 5 6 13:57 cpu.profile
drwxr-xr-x 4 Diego Staff 136 5 6 11:47 popcnt
-rwxr-xr-x 1 Diego staff 3424176 5 6 13:57 Popcnt.test
➜ Testbenchmark
Production Cpu.profile asking price and Popcnt.test file
➜ testbenchmark ll Total
6704
drwxr-xr-x 5 Diego 5 6 13:57.
Drwxr-xr-x 3 Diego staff 102 5 6 11:12.
-rw-r--r-- 1 Diego staff 5200 5 6 13:57 cpu.profile
drwxr-xr-x 3 Diego Staff 102 5 6 14:01 popcnt
-rwxr-xr-x 1 Diego staff 3424176 5 6 13:57 Popcnt.test
➜ Testbenchmark
Go tool pprof popcnt.test cpu.profile Enter interactive mode
➜testbenchmark Go tool pprof popcnt.test cpu.profile Entering Interactive mode (type ' help ' for Commands) (PPROF) Top 1 880ms of 1880ms Total (100%) flat flat% sum% cum cum% 1790ms 95.21% 95.21% 1790ms 95.21% app/ Testbenchmark/popcnt. benchmarkpopcnt 90ms 4.79% 100% 90ms