For us programmers, how to improve the quality of code must be our top priority. Not only do you need to be able to write good business code, but you also need to ensure the quality of your code. Test cases are a great tool for improving the quality of our code.

Through the test, we can timely find our program design logic errors, and can take over the project of other programmers students understand the function of help.

This article focuses on the Testing package in the Go language. It requires us to create a new file with *_test.go and name the function TestXxx in the file. Then execute the function via go test [flags] [packages].

$ ls db.go db_test.go $ cat db_test.go package db import "testing" func TestGetUser(t *testing.T) { user, err := GetUser("[email protected]") if err ! = nil { t.Fatal(err) } t.Log(user) }Copy the code

It also provides three types of functions: test function T, benchmark function B, and instance function Example.

The Test Test

Function tests, whose basic signature is:

func TestName(t *testing.T){
    // ...
}
Copy the code

The name of the Test function must start with Test, and the optional suffix must not start with a lowercase letter, usually the same as the name of the function being tested.

The testing.t type has the following methods:

// Prints logs. For tests, prints on failure or when the -test.v flag is specified. Func (c *T) Log(args... interface{}) func (c *T) Logf(format string, args ... Func (c *T) Fail() func (c *T) Fail() runtime.goexit But continue to perform other functions or benchmarks. Func (c *T) FailNow() // Whether the return function fails func (C *T) Failed() bool // Equivalent to t.log + t.ail func (c *T) Error(args... Interface {}) // equivalent to t. loggf + t. ail func (c *T) Errorf(format string, args... Interface {}) // equivalent to t.log + t.felnow Fatal(ARgs... Interface {}) // equivalent to t. logof + t. ailNow func (c *T) Fatalf(format string, args... Interface {}) // marks the calling function token as a test helper function. Func (c *T) Helper() // Returns the Name of the running test or benchmark func (c *T) Name() string // Used to indicate that the current test will only be tested in Parallel with other tests with Parallel methods. Func (t * t) Parallel() Func (t * t) Run(name string, f func(t * t)) bool t.log + t. SkipNow func(c * t) Skip(args... Interface {}) // mark the test as skipped and call Runtime.goexit to exit the test. Func (c *T) SkipNow() // equivalent to t.loggf + t.skipnow func (c *T) Skipf(format string, args... Interface {}) // Report whether to ignore func (C *T) deflectors () boolCopy the code

Benchmark test

Function tests, whose basic signature is:

func BenchmarkName(b *testing.B){
    // ...
}
Copy the code

The name of the test function must start with Benchmark, and the optional suffix must not start with a lowercase letter, usually the same as the name of the test function.

Type B takes a parameter N, which can be used to just benchmark the number of iterations run. Benchmarks and tests, benchmarks always log.

type B struct {
    N int
    // contains filtered or unexported fields
}
Copy the code

Benchmarks have more functions than tests:

func (c *B) Log(args ... interface{}) func (c *B) Logf(format string, args ... interface{}) func (c *B) Fail() func (c *B) FailNow() func (c *B) Failed() bool func (c *B) Error(args ... interface{}) func (c *B) Errorf(format string, args ... interface{}) func (c *B) Fatal(args ... interface{}) func (c *B) Fatalf(format string, args ... interface{}) func (c *B) Helper() func (c *B) Name() string func (b *B) Run(name string, f func(b *B)) bool func (c *B) Skip(args ... interface{}) func (c *B) SkipNow() func (c *B) Skipf(format string, args ... Function to bounce about. To bounce about. Interface {}) func (c *B) Skipped() bool // Turn on the memory statistics function of the current benchmark, similar to using the -test.benchmem setting, // but ReportAllocs only affects benchmarks that call that function. // Clear the elapsed benchmark time and memory allocation counter. This method has no effect on a running timer. Func (b * b) ResetTimer() example: func BenchmarkBigLen(b *testing.b) {big := NewBig() b.resettimer () for I := 0; func (b * b) ResetTimer() for I := 0; i < b.N; I++ {big.len ()}} // executes the given benchmark in parallel. RunParallel creates multiple goroutines and assigns b.N iterations to these goroutines, where the default number of goroutines is GOMAXPROCS. Users who want to increase parallelism for non-CPU-bound benchmarks can call SetParallelism before RunParallel. RunParallel is usually used with the -CPU flag. // The body function will be executed in each goroutine. This function needs to set the local state of all goroutines, and iterate until pb.next returns false. Because the StartTimer, StopTimer, and ResetTimer functions all have global effects, the body function should not call these functions; In addition, the body function should not call the Run function. Func (* b) RunParallel(body func(*PB))  func BenchmarkTemplateParallel(b *testing.B) { templ := template.Must(template.New("test").Parse("Hello, {{.}}!" )) b.RunParallel(func(pb *testing.PB) { var buf bytes.Buffer for pb.Next() { buf.Reset() templ.Execute(&buf, "World")}})} // Records the number of bytes processed in a single operation. After calling this method, the benchmark will report ns/op and MB/s func (b * b) SetBytes(n int64) // Set the number of goroutines used by RunParallel to P *GOMAXPROCS, If p is less than 1, the call has no effect. // CPU-bound benchmarks usually do not call this method. Func (b * b) SetParallelism(p int) // This function is called automatically at the start of the benchmark and can be resumed for timing after StopTimer is called. Func (b * b) StartTimer() // Stops the timer for the test. func (b *B) StopTimer()Copy the code

Example test

Example functions can help us write an example and compare it with the output:

func ExampleHello() { fmt.Println("hello") // Output: hello } func ExampleSalutations() { fmt.Println("hello, and") fmt.Println("goodbye") // Output: Unordered output func ExamplePerm() {for _, value := range Perm(4) { fmt.Println(value) } // Unordered output: 4 // 2 // 1 // 3 // 0 }Copy the code

We need to know about the sample functions:

  • The signature of the function needs to beExampleAt the beginning
  • There are two kinds of Output contrast: ordered Output and Unordered Output
  • If the function has no output comment, it will not be executed

The official rules for naming us are:

Func example () {... } // example of a function F func ExampleF() {... } // an example of type T } // example of a method M of type T func ExampleT_M() {... } // If you need to provide more than one example of the above four types, you can add the suffix // the suffix must be lowercase func Example_suffix() {... } func ExampleF_suffix() { ... } func ExampleT_suffix() { ... } func ExampleT_M_suffix() { ... }Copy the code

The child test

We also talked about Test and Benchmark’s Run methods, which execute child tests.

func TestFoo(t *testing.T) {
    // <setup code>
    t.Run("A=1", func(t *testing.T) { ... })
    t.Run("A=2", func(t *testing.T) { ... })
    t.Run("B=1", func(t *testing.T) { ... })
    // <tear-down code>
}
Copy the code

Each child test can be represented by a unique name: a combination of the name of the top-level test and the name sequence passed to Run, separated by a /.

Go test-run Foo # Match the top-level tests associated with Foo, For example, TestFooBar go test-run Foo/A= # matches the top-level tests associated with Foo and matches the sub-tests A= go test-run /A=1 # Matches all top-level tests and matches their sub-tests A=1Copy the code

Subtests can also be used to control parallelism. Parent tests can only be completed after all child tests have been completed. In this example, all tests are parallel to each other and run only with each other, regardless of other top-level tests that might be defined:

func TestGroupedParallel(t *testing.T) { for _, tc := range tests { tc := tc // capture range variable t.Run(tc.Name, func(t *testing.T) { t.Parallel() ... }}})Copy the code

A run does not return until parallel child tests are complete, which provides a way to clean up after a set of parallel tests:

func TestTeardownParallel(t *testing.T) {
    // This Run will not return until the parallel tests finish.
    t.Run("group", func(t *testing.T) {
        t.Run("Test1", parallelTest1)
        t.Run("Test2", parallelTest2)
        t.Run("Test3", parallelTest3)
    })
    // <tear-down code>
}
Copy the code

The Main test

Sometimes we also need to test from the main function:

Func TestMain(m * test.m)  func TestMain(m *testing.M) { // call flag.Parse() here if TestMain uses flags os.Exit(m.Run()) }Copy the code

HTTP test

Go language is the current web development is a lot, so after we have the function testing, HTTP testing how to do?

The Go standard library provides an HTTPTest library that makes it easy to complete HTTP tests.

1. Test Handle

package main import ( "fmt" "io" "io/ioutil" "net/http" "net/http/httptest" ) var HandleHelloWorld = func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "<html><body>Hello World! </body></html>") } func main() { req := httptest.NewRequest("GET", "http://example.com/foo", nil) w := httptest.NewRecorder() HandleHelloWorld(w, req) resp := w.Result() body, _ := ioutil.ReadAll(resp.Body) fmt.Println(resp.StatusCode) fmt.Println(resp.Header.Get("Content-Type")) fmt.Println(string(body)) }Copy the code

2, TLS server?

package main import ( "fmt" "io/ioutil" "log" "net/http" "net/http/httptest" ) func main() { ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, client") })) defer ts.Close() client := ts.Client() res, err := client.Get(ts.URL) if err ! = nil { log.Fatal(err) } greeting, err := ioutil.ReadAll(res.Body) res.Body.Close() if err ! = nil { log.Fatal(err) } fmt.Printf("%s", greeting) }Copy the code

3. How to test commonly used HTTP frameworks?

// Package main provides ...
package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"

    "github.com/gin-gonic/gin"
)

func main() {
    engine := gin.Default()
    engine.GET("/hello", func(c *gin.Context) { c.String(http.StatusOK, "Hello") })
    engine.GET("/world", func(c *gin.Context) { c.String(http.StatusOK, "world") })

    req := httptest.NewRequest(http.MethodGet, "/hello", nil)
    w := httptest.NewRecorder()

    engine.ServeHTTP(w, req)

    fmt.Println(w.Body.String())
}
Copy the code

Link to this article: deepzz.com/post/study-… , join the comments »

–EOF–

Published on 2018-05-09 23:31:00, with the tag “go, test” added.

Other articles in the Go Pit series»

  • Some pits for the Go keyword defer (Aug 27, 2017)
  • Golang Sync Package (Aug 19, 2017)
  • Golang bloggers walk through some pits about error (May 14, 2017)
  • Glide command, How to Use Glide, Glide. Lock (Feb 09, 2017)
  • Golang Package Management Tool Glide, You Deserve it (Feb 07, 2017)