Go language study notes 8 - testing and performance tuning
Debugging Sucks! Testing Rocks!
traditional test
- Test logic mixed with test data
- The error message is not clear
- Once a data error test all ends
form-driven testing
- Separate test data and test logic
- clear error message
- can partially fail
- The syntax of go language makes it easier for us to practice table-driven testing
func calcTriangle(a, b int) int {
var c int
c = int(math.Sqrt(float64(a*a + b*b)))
return c
}
func TestTriangle(t *testing.T) {
tests := []struct{
a, b, c int }{
{
3, 4, 5},
{
5, 12, 13},
{
8, 15, 17},
{
12, 35, 37},
{
30000, 40000, 50000},
}
for _, tt := range tests {
if actual := calcTriangle(tt.a, tt.b); actual != tt.c {
t.Errorf("calcTriangle(%d,%d);"+
"got %d;expected %d",
tt.a, tt.b, actual, tt.c)
}
}
}
code coverage
Check the code coverage of the test code.
Green is covered, red is not covered
Performance Testing
func BenchmarkSubstr(b *testing.B) {
aa := 30000
bb := 40000
cc := 50000
for i := 0; i < b.N; i++ {
actual := calcTriangle(aa, bb)
if actual != cc {
b.Errorf("calcTriangle(%d,%d);"+
"got %d;expected %d",
aa, bb, actual, cc)
}
}
}
result:
pprof performance tuning
Command line command:
- go test -bench . -cpuprofile cpu.out Get cpu performance log file cpu.out
- go tool pprof cpu.out View log file
- For web visualization log files, Graphviz needs to be installed first, and the link can be downloaded and installed directly below.
Graphviz
Visualize the result:
How to optimize map
Space for time
a:=make([]int,0xffff)
Assuming that the maximum Chinese character is 0xFFFF, here an array of 0xFFFF size can be used to store all characters
: a['e']=1 (essential: a[0x65]=1); a['class']=1 (Essential: a[0x8BFE]=1)
Performance Tuning Steps
- -cpuprofile: get performance data
- go tool pprof: View performance data
- Where is the analysis slow
- optimize code
http test
two methods
- By using fake Request/Response
- through the server
The tested functions refer to the server unified error handling in the previous note 7
func errPanic(writer http.ResponseWriter,
request *http.Request) error {
panic(123)
}
type testingUserError string
func (e testingUserError) Error() string {
return e.Message()
}
func (e testingUserError) Message() string {
return string(e)
}
func errUserError(writer http.ResponseWriter,
request *http.Request) error {
return testingUserError("user error")
}
func errNotFound(writer http.ResponseWriter,
request *http.Request) error {
return os.ErrNotExist
}
func errNotPermission(writer http.ResponseWriter,
request *http.Request) error {
return os.ErrPermission
}
func errUnknown(writer http.ResponseWriter,
request *http.Request) error {
return errors.New("unknown error")
}
func noError(writer http.ResponseWriter,
request *http.Request) error {
fmt.Fprintln(writer, "no error")
return nil
}
var tests = []struct {
h appHandler
code int
message string
}{
{
errPanic, 500, "Internal Server Error"},
{
errUserError, 400, "user error"},
{
errNotFound, 404, "Not Found"},
{
errNotPermission, 403, "Forbidden"},
{
errUnknown, 500, "Internal Server Error"},
{
noError, 200, "no error"},
}
//用假的request,response,速度快
func TestErrWrapper(t *testing.T) {
for _, tt := range tests {
f := errWrapper(tt.h)
response := httptest.NewRecorder()
request := httptest.NewRequest(
http.MethodGet, "http://www.imooc.com", nil)
f(response, request)
verifyResponse(response.Result(), tt.code, tt.message, t)
}
}
//真正起一个server,测试力度更大
func TestErrWrapperInServer(t *testing.T) {
for _, tt := range tests {
f := errWrapper(tt.h)
server := httptest.NewServer(http.HandlerFunc(f))
resp, _ := http.Get(server.URL)
verifyResponse(resp, tt.code, tt.message, t)
}
}
func verifyResponse(resp *http.Response, expectedCode int, expectMsg string, t *testing.T) {
b, _ := ioutil.ReadAll(resp.Body)
body := strings.Trim(string(b), "\n")
if resp.StatusCode != expectedCode || body != expectMsg {
t.Errorf("expected(%d,%s);"+
"got(%d,%s)",
expectedCode, expectMsg,
resp.StatusCode, body)
}
}
Generate documentation
Write documentation with comments
Add Example to tests
Use go doc/godoc to view/generate documentation
godoc -http :6060
Generate documentation, access via http://localhost:6060/pkg/
Notes
// An FIFO queue.
type Queue []int
// Pushes the element into the queue.
// e.g. q.Push(123)
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
// Pops element from head.
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
// Returns if the queue is empty or not.
func (q *Queue) IsEmpty() bool {
return len(*q)==0
}
Sample code:
can also be used as a test
func ExampleQueue_Pop() {
q := Queue{
1}
q.Push(2)
q.Push(3)
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
// Output:
// 1
// 2
// false
// 3
// true
}
Generate documentation: