The introduction
- Learn about go unit testing
- Why write unit tests
- How should unit tests be written in the GIN framework
- How does GIN write elegant unit tests
1. Unit testing knowledge
-
The unit test file name must be xxx_test.go(where XXX is the business logic program)
-
It is divided into basic test, benchmark test and case test
-
The function name for the underlying test must be Testxxx(XXX can be used to identify business logic functions)
-
The benchmark must appear as the BenchmarkXXX function name
-
The case test must come out with the ExampleXXX function name
-
Unit test function parameter must be T *testing. t (testing framework strong requirement)
-
Test and test files in a package (I’ll put tests in a separate directory)
-
Test case files do not participate in normal source compilation and are not included in the executable
-
You must import the testing package
-
Common commands have many people said, I will provide a document
2. Why write unit tests
Ensure the quality of the code, ensure that every function is running, the results are correct, and ensure that the performance of the written code is good. Using unit tests at the same time is intuitive and can improve development efficiency. You don’t need to compile your code every time, and then go to Postman to simulate parameter tests. It also helps you with performance testing and code coverage, export test reports, and more
How to write unit tests in the GIN framework
TestMain
When writing tests, you sometimes need to do extra setup or teardown before or after the test. Sometimes, tests also need to control the code that runs on the main thread. To support these requirements, the Testing package provides the TestMain function:
package tests
import (
"testing"
"fmt"
"os"
"github.com/gin-gonic/gin"
"go-api/config"
)
func setup(a) {
gin.SetMode(gin.TestMode)
fmt.Println(config.AppSetting.JwtSecret);
fmt.Println("Before all tests")}func teardown(a) {
fmt.Println("After all tests")}func TestMain(m *testing.M) {
setup()
fmt.Println("Test begins....")
code := m.Run() // If this sentence is not added, only Main is executed
teardown()
os.Exit(code)
}
Copy the code
Gin unit test case
package main
func setupRouter(a) *gin.Engine {
r := gin.Default()
r.GET("/ping".func(c *gin.Context) {
c.String(200."pong")})return r
}
func main(a) {
r := setupRouter()
r.Run(": 8080")}Copy the code
package main
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func TestPingRoute(t *testing.T) {
router := setupRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET"."/ping".nil)
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.Equal(t, "pong", w.Body.String())
}
Copy the code
4. How can GIN write elegant unit tests
Use the Table – Driven Test
func TestFib(t *testing.T) {
var fibTests = []struct {
in int // input
expected int // expected result
}{
{1.1},
{2.1},
{3.2},
{4.3},
{5.5},
{6.8},
{7.13}},for _, tt := range fibTests {
actual := Fib(tt.in)
ifactual ! = tt.expected { t.Errorf("Fib(%d) = %d; expected %d", tt.in, actual, tt.expected)
}
}
}
Copy the code
Encapsulate some Test Helpers
package tests
import (
"io"
"net/http"
"net/http/httptest"
"bytes"
"fmt"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"go-api/routes"
"go-api/tool"
)
type TestCase struct {
code int / / status code
param string / / parameters
method string // Request type
desc string / / description
showBody bool // Whether to show returns
errMsg string // Error message
url string / / links
content_type string //
ext1 interface{} // Customize 1
ext2 interface{} // Customize 2
}
func NewBufferString(body string) io.Reader {
return bytes.NewBufferString(body)
}
func PerformRequest(mothod, url, content_type string, body string) (c *gin.Context, r *http.Request, w *httptest.ResponseRecorder) {
router := routes.InitRouter()
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
r = httptest.NewRequest(mothod, url, NewBufferString(body))
c.Request = r
c.Request.Header.Set("Content-Type", content_type)
router.ServeHTTP(w, r)
return
}
func call(t *testing.T,testcase []TestCase){
for k, v := range testcase {
_, _, w := PerformRequest(v.method, v.url, v.content_type, v.param)
//assert.Contains(t, w.Body.String(),fmt.Sprintf("\"error_code\":%d",v.code))
fmt.Println()
fmt.Printf("%d test case: %s", k+1, v.desc)
if v.showBody {
fmt.Printf("Interface returns %s", w.Body.String())
fmt.Println()
}
s := struct {
Error_code int `json:"error_code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}{}
err := tool.JsonToStruct([]byte(w.Body.String()), &s)
assert.NoError(t, err)
assert.Equal(t, v.errMsg, s.Msg, "Inconsistent error messages.")
assert.Equal(t, v.code, s.Error_code, "Inconsistent error codes")}}Copy the code
Using subtests
To control the sequence of nested tests and execution and resolve test suite dependencies
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
5. Go test command
- -bench regexp performs corresponding benchmarks, such as -bench=.
- -benchtime s Specifies the performance test time. The default time is one second
- -cover Opens the test coverage;
- -run regexp only runs functions that match regexp. For example, -run=Array then executes functions that start with Array;
- -v Displays detailed test commands.
- -cache=1 Removes the cache
- -args brings the parameters following -args to the test
- -json Converts the output to JSON
- The -o -o argument specifies the generated binary executable and executes the test without removing the program at the end of the test
- -count Specifies the number of execution times. The default one
-bench
func BenchmarkMakeSliceWithoutAlloc(b *testing.B)
func BenchmarkMakeSliceWithPreAlloc(b *testing.B)
func BenchmarkSetBytes(b *testing.B)
Copy the code
-agrs
func TestArgs(t *testing.T) {
if! flag.Parsed() { flag.Parse() } argList := flag.Args()// flag.args () returns all arguments following -args as slices, with each element representing one argument
for _, arg := range argList {
if arg == "cloud" {
t.Log("Running in cloud.")}else {
t.Log("Running in other mode.")}}Copy the code
6, the end
Let me share with you
Common commands and document address portal
🏆 technology project phase ii | and I Go those things…