# 11.3 Writing test cases In the course of development, a very important step is to test our code to ensure its quality and integrity. We need to make sure that every function returns the expected result, and that our code performs optimally. We already know that the focus of unit tests is to find logical errors in the design or implementation of programs. They are used to detect and expose problems in code early on so that we can more easily fix them, before they get out of hand. We also know that performance tests are conducted for the purpose of optimizing our code so that it is stable under load, and can maintain a high level of concurrency. In this section, we'll take a look at some commonly asked questions about how unit and performance tests are implemented in Go. The Go language comes with a lightweight testing framework called `testing`, and we can use the `go test` command to execute unit and performance tests. Go's `testing` framework works similarly to testing frameworks in other languages. You can develop all sorts of test suites with them, which may include tests for edge cases, benchmarking, etc. Let's learn about testing in Go, step by step. ## How to write test cases Since the `go test` command can only be executed in a directory containing all corresponding files, we are going to create a new project directory `gotest` so that all of our code and test code are in the same directory. Let's go ahead and create two files in the directory called gotest.go and gotest_test.go 1. Gotest.go: This file declares our package name and has a function that performs a division operation:
package gotest
	
	import (
		"errors"
	)
	
	func Division(a, b float64) (float64, error) {
		if b == 0 {
			return 0, errors.New("Divisor can not be 0")
		}
		return a / b, nil
	}
2. Gotest_test.go: This is our unit test file. Keep in mind the following principles for test files: - File names must end in `_test.go` so that `go test` can find and execute the appropriate code - You have to import the `testing` package - All test case functions begin with `Test` - Test cases follow the source code order - Test functions of the form `TestXxx()` take a `testing.T` argument; we can use this type to record errors or to get the testing status - In functions of the form `func TestXxx(t * testing.T)`, the `Xxx` section can be any alphanumeric combination, but the first letter cannot be a lowercase letter [az]. For example, `Testintdiv` would be an invalid function name. - By calling one of the `Error`, `Errorf`, `FailNow`, `Fatal` or `FatalIf` methods of `testing.T` on our testing functions, we can fail the test. In addition, we can call the `Log` method of `testing.T` to record the information in the error log. Here is our test code: package gotest import ( "testing" ) func Test_Division_1(t *testing.T) { // try a unit test on function if i, e := Division(6, 2); i != 3 || e != nil { // If it is not as expected, then the test has failed t.Error("division function tests do not pass ") } else { // record the expected information t.Log("first test passed ") } } func Test_Division_2(t *testing.T) { t.Error("just does not pass") } When executing `go test` in the project directory, it will display the following information: --- FAIL: Test_Division_2 (0.00 seconds) gotest_test.go: 16: is not passed FAIL exit status 1 FAIL gotest 0.013s We can see from this result that the second test function does not pass since we wrote in a dead-end using `t.Error`. But what about the performance of our first test function? By default, executing `go test` does not display test results. We need to supply the verbose argument `-v` like `go test -v` to display the following output: === RUN Test_Division_1 --- PASS: Test_Division_1 (0.00 seconds) gotest_test.go: 11: first test passed === RUN Test_Division_2 --- FAIL: Test_Division_2 (0.00 seconds) gotest_test.go: 16: is not passed FAIL exit status 1 FAIL gotest 0.012s The above output shows in detail the results of our test. We see that the test function 1 `Test_Division_1` passes, and the test function 2 `Test_Division_2` fails, finally concluding that our test suite does not pass. Next, we modify the test function 2 with the following code: func Test_Division_2(t *testing.T) { // try a unit test on function if _, e := Division(6, 0); e == nil { // If it is not as expected, then the error t.Error("Division did not work as expected.") } else { // record some of the information you expect to record t.Log("one test passed.", e) } } We execute `go test-v` once again. The following information should now be displayed -the test suite has passed~: === RUN Test_Division_1 --- PASS: Test_Division_1 (0.00 seconds) gotest_test.go: 11: first test passed === RUN Test_Division_2 --- PASS: Test_Division_2 (0.00 seconds) gotest_test.go: 20: one test passed. divisor can not be 0 PASS ok gotest 0.013s ## How to write stress test Stress testing is used to detect function ( method ) performance, and writing unit functional testing method similar are not mentioned here, but need to pay attention to the following points: - Pressure test must follow the following format, where XXX can be any combination of alphanumeric, but can not be the first letter lowercase letters func BenchmarkXXX (b *testing.B){...} - `Go test` does not default to perform stress tests of function, if you want to perform stress tests need to bring arguments `-test.bench`, syntax: `-test.bench =" test_name_regex "`, such as `go test-test. bench = ". *" ` all of the stress test indicates the test function - In the pressure test, please remember to use the loop body `testing.BN`, so that the test can be normal operation - File name must end with `_test.go` Here we create a stress test file webbench_test.go, the code is as follows: package gotest import ( "testing" ) func Benchmark_Division(b *testing.B) { for i := 0; i < bN; i++ { // use bN for looping Division(4, 5) } } func Benchmark_TimeConsumingFunction(b *testing.B) { b.StopTimer() // call the function to stop the stress test time count // Do some initialization work, such as reading file data, database connections and the like, // So we test these times do not affect the performance of the function itself b.StartTimer() // re- start time for i := 0; i < b.N; i++ { Division(4, 5) } } We execute the command `go test-file webbench_test.go-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 a function of stress tests carried out, the first show `Benchmark_Division` executed 500 million times the average execution time is 7.76 ns, the second shows the `Benchmark_TimeConsumingFunction` executed 500 million each, the average execution time is 7.80 ns. Finally a show total execution time. ## Summary On the face of unit testing and stress testing of learning, we can see the `testing` package is very lightweight, and write unit tests and stress test is very simple, with built-in `go test` command can be very convenient for testing, so every time we have finished modifying code, click go test execution can simply complete regression testing. ## Links - [Directory](preface.md) - Previous section: [Debugging by using GDB](11.2.md) - Next section: [Summary](11.4.md)