Unit testing

  • Unit Tests (UT) are an integral part of a good project, especially in projects with frequent changes and collaboration. At one point or another, you’ve had your app die or your service go down because of your submission. For example, I once printed SQL and forgot to close it, causing all the interfaces to fail (sad). The other thing is if you optimize some code logic, optimize some function. So how do we make sure we’re right? A few test cases might solve the problem.

Name the test file

  • The go language supports unit tests, and test files are filenames ending in _test.go

Naming of test cases

  • A Test case name is usually Test plus the name of the method to be tested. For example, TestAdd() takes one and only one parameter, in this caset *testing.T

Simple introduction

  • Let’s write an Add function and create the file add.go
package main

import "fmt"

func main() {
	sum := Add(1, 2)
	fmt.Println(sum)
}

func Add(a int, b int) int {
	return a + b
}

Copy the code
  • Let’s create an add_test.go test file in the add.go directory
package main

import (
	"testing"
)

func TestAdd(t *testing.T) {
	testData := []struct {
		a int
		b int
		c int
	}{
		{1, 2, 3},
		{4, 5, 9},
		{50, 5, 5},
	}
	ans := 0
	for _, data := range testData {
		ifans = Add(data.a, data.b); ans ! = data.c { t.Errorf("%d + %d expected %d,but %d got", data.a, data.b, data.c, ans)
		}
	}

}
Copy the code

Let’s take a look at the IDE debugging tools, which we can also run at the command

  • Run Execution script
  • Debug Enables the Debug mode
  • Coverprofile code coverage
  • CPU Profiler Cup analysis
  • Memory Profiler Memory Profiler objects, heap analysis, Memory leaks, etc
  • Blocking Profiler logs goroutine Blocking, waiting and synchronization, timer/channel communication, etc
  • Mutex Profiler Mutex analysis, including various race cases

  • I’ll run it through the IDE

  • We can also use the IDE’s Debug function to interrupt debugging

  • To test the command line, first go to the add.go directory

Code coverage

  • IDE debugging

  • Let’s try it again using the command line
zhangguofu@zhangguofudeMacBook-Pro add (master) $ go test-Coverprofile =c.out -- FAIL: TestAdd (0.00s) add_test.go: 20:50 + 5 Expected 5,but 55 got FAIL coverage: 33.3% of the statementsexitStatus 1 FAIL goapp/ SRC/Learngo /add 0.502s zhangguofu@zhangguofudeMacBook-Pro add (master) $ls add.go add_test.go c.out  zhangguofu@zhangguofudeMacBook-Pro add (master) $ cat c.out mode:setGoapp/SRC/learngo/add/add go: 5.13, 8.2 2 0 goapp/SRC/learngo/add/add go: 10.28, 12.2 1 to 1Copy the code

We found an extra C.out file, but we could not understand the content in it. We used the tool Go Tool Cover to check the help of related commands for code coverageWe use ‘go Tool cover-html =c.out’ to open an HTML page with our code coverage

The performance test

  • Let’s test the performance with a function that gets the longest unrepeated string
  • The function code is in the add.go file
// func Repeat(s string) int {// lastOccur := make(map[string]int) start := 0 Max := 0for k, v := range []rune(s) {
		if index, ok := lastOccur[string(v)]; ok && start <= index {
			start = index + 1
		}
		if max < k-start+1 {
			max = k - start + 1
		}
		lastOccur[string(v)] = k
	}
	return max
}
Copy the code
  • The performance test code is in the add_test.go file
func BenchmarkRepeat(b *testing.B) {
	str := "No rain, no rainbow? No one can casually succeed."
	data := struct {
		content string
		res     int
	}{
		str,
		17,
	}
	for i := 0; i < b.N; i++ {
		iflen := Repeat(data.content); len ! = 17 { b.Error("the program is wrong")}}}Copy the code

  • It turns out to be about 3ms, so let’s stretch it out a little bit
func BenchmarkRepeat(b *testing.B) {
	str := "No rain, no rainbow? No one can be casual."

	for i := 0; i < 20; i++ {
		str += str
	}

	b.Logf("the len of str is %d", len(STR)) b.resettimer () // Ignore the calculation time abovefor i := 0; i < b.N; i++ {
		Repeat(str)
	}
}
Copy the code
  • We found that for computing a non-repeating string in a 6m file, the program executed about 16 seconds, which feels slow, right, but we want to see where the program was spending its time

  • Similar to the code coverage command. I follow orders
zhangguofu@zhangguofudeMacBook-Pro add (master) $ go test-bench . -cpuprofile cpu.out goos: darwin goarch: amd64 pkg: goapp/src/learngo/add BenchmarkRepeat-8 1 1541179761 ns/op --- BENCH: BenchmarkRepeat-8 add_test.go:33: The len of STR is 62914560 PASS OK goapp/ SRC /learngo/add 2.077s zhangguofu@zhangguofudeMacBook-Pro add (master) $ls add.go add.test add_test.go c.out cpu.out zhangguofu@zhangguofudeMacBook-Pro add (master) $ less cpu.out"cpu.out" may be a binary file.  See it anyway? 
Copy the code

Found generated CPU. Out is a binary file that we usego tool pprof cpu.outView and enter interactive modeI typed it in Web format and I got an error saying I need to install Graphviz, so go aheadDownload addressIf the installation fails, please refer to this articleFix brew installation failure

  • Input again go tool pprof cpu.outEnter the web

There is no error, but here I run into one problem after another. My virtual machine (Win10 on MAC) will open SVG files by default, annoying solutionAnd set Chrome to the default opening mode

  • Finally, we open this file, the thicker the arrow, the longer the time, we can optimize our program according to the results, for example, here, we see string encode decode is a waste of time, so can we choose a more appropriate data type to store our data, So let’s say we haveABC I love ChinaSo in the calculation, it is very troublesome to convert back and forth. Can we convert to INT32 for calculation?

  • So much for unit testing and performance testing for Golang. Like can like oh!!