当前位置:网站首页>Go unit testing introductory practice
Go unit testing introductory practice
2022-06-29 13:57:00 【Hua Weiyun】
Go unit testing
Go Unit test code for different files in , Write in its corresponding xxx_test.go file , The unit test file can contain three types of functions , Unit test functions 、 Benchmark functions and sample functions . This article only introduces Unit test functions .
This article will start from different demand scenarios , Take a quick look with specific examples Go Unit test writing .
(ps: For function and method piling, it is recommended to use gomonkey,bou.ke Of monkey The framework certificate has expired )
1. The basic composition of unit test
Simple test function example :
// file name demo.gopackage testutilimport "errors"type calculator struct {}func (c *calculator) multiplication(a, b int) (int, error) { return a*b, nil}func (c *calculator) Division(a, b int) (int, error) { if b == 0 { return 0, errors.New("error: divide by 0") } return a/b, nil}// Create... In the same directory demo_test.go file package testutilimport ( "github.com/stretchr/testify/require" "testing")// Basic structure : Construct input , Execute function , Judge the result func Test_calculator_Division(t *testing.T) { var cal calculator // Define input numA := 2 numB := 1 // Expected results want := 2 // Execute function res, err := cal.Division(numA, numB) // check if err != nil { t.Errorf("Division() error = %v, wantErr: nil", err) } if res != want { t.Errorf("Division() result = %v, want %v", res, want) }}Skip some tests :
Add the following judgment to the test function , perform go test -short The following test cases will be skipped
func Test_calculator_Division(t *testing.T) { if testing.Short() { t.Skip("short The test case will be skipped in mode ") } ...}Create multiple test cases :
- Subtest :t.Run(“case name”, func(t *testing){…}), among case name Used to distinguish different test cases
- Table driven testing :for range { t.Run(“case name”, func(t *testing){…}) }
- Parallel processing test : stay func Join at the beginning t.Parallel(); notes : Same by default package There is serial operation , Different package Parallel execution between
- Use the help function to extract duplicate logic : stay _test.go Add... At the beginning of the non test function defined in the file t.Helper() It can make the error report more clear , When an error is reported, the caller information will be output , Instead of just outputting information about the called function
- PS: t.Error and t.Fatal The difference lies in , The former keeps on making mistakes
// Multiple test cases // Table driven // Parallel execution func Test_calculator_Division(t *testing.T) { // Parallel execution is also performed in the package t.Parallel() // Define input type args struct { a int b int } // Multiple test cases : Use case name 、 Enter the reference 、 Expected results 、 Whether the expectation is wrong tests := []struct { name string args args want int wantErr bool }{ {"case name", args{2, 1}, 2, false}, } // Traverse the test case for _, tt := range tests { // Use t.Run() Perform subtests t.Run(tt.name, func(t *testing.T) { c := &calculator{} got, err := c.Division(tt.args.a, tt.args.b) if (err != nil) != tt.wantErr { t.Errorf("Division() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("Division() got = %v, want %v", got, tt.want) } }) }}Multiple tests depend on a variable :
// Can be used to initialize global variables ; An entity on which multiple test functions depend ( Variable , database ,GRPC Service etc. ), Initialize before the test starts , To avoid duplicate code // Each code package can have one TestMain, It will execute all tests in the package before they start func TestMain(m *testing.M) { setUp() // Self implemented resource initialization code , It is generally used to assign values to global variables code := m.Run() tearDown() // Cleaning up resources , Such as : close DB, Delete temporary files , close http Connections etc. os.Exit(code)}Quickly generate test files :
Use Goland Right click Generate Generate test code for functions or entire files
Use gotests Generate :gotests -all -w demo.go
Test coverage :
Usually we use the coverage of statements , That is, the proportion of the total code that is run at least once in the test .
perform :go test -cover perhaps go tool cover -html=c.out
have access to goland Right click to execute test with coverage, You can visually see the test coverage in the package
testify: Assertion /require /assert:
testify It is very popular in a community Go Unit test kit , One of the most used functions is the assertion tool it provides —testify/assert or testify/require.( You can also choose more functions goconver)
// slice Comparative judgment if !reflect.DeepEqual(got, s.want) { t.Errorf("expected:%#v, result:%#v", s.want, res)}// Use testify Just one line of judgment assert.Equal(t, res, s.want)When there are multiple assertion statements :
// establish assert object assert := assert.New(t)// No need to pass in Testing.T Parameters assert.Equal(res, want)require Have assert All assertion functions , The only difference between them is — require The test will be terminated immediately in case of failure .
2. Interface piling
Mock: The interface test
In addition to external dependencies such as networks and databases , We often use various interface types in our development . Use gomock It can better pile the interface ,gomonkey It's fine too , Interface test writing is just a little more complicated .
Mockgen
mockgen The command is used for a given that contains mock The interface of Go Source file , Generate mock Class source code .
install :$GOPATH/bin Has been added to the environment variable ;GO111MODULE=on; go get github.com/golang/mock/[email protected]
function : Source mode and reflection mode
mockgen -source=db.go -destination=mocks/db_mock.go -package=mocksTest function writing :
func TestGetFromDB(t *testing.T) { // establish gomock controller , Used to record subsequent operation information ctrl := gomock.NewController(t) // call mockgen Generate... In the code NewMockDB Method m := mocks.NewMockDB(ctrl) // Pile driving (stub) // When it comes to Get The argument to the function is expect input When to return to 1 and nil m. EXPECT(). xxx(gomock.Eq("expect input")). // Function method xxx, Specify input parameters =“expect input” Return(1, nil). // Return value 1,nil Times(1) // Call the number // Pass in mock Interface m, Call function if v := GetFromDB(m, "expect input"); v != 1 { t.Fatal() } // If the parameter passed in during the function call is not “expect input”, Then piling will not take effect , Instead, call the original function }Input parameters can specify specific values , You can also use the following method to specify :
- gomock.Eq(value): Represents an equivalent to value Parameter of value
- gomock.Not(value): It means a non value Parameter of value
- gomock.Any(): A parameter that represents an arbitrary value
- gomock.Nil(): A parameter that represents a null value
Different return methods can be set for the return value :
- Return(): Returns the specified value
- Do(func): Perform the operation , Ignore return value
- DoAndReturn(func): Execute and return the specified value
Set the number of times expected to be called :
- Times() Assertion Mock The number of times the method was called .
- MaxTimes() Maximum number of times .
- MinTimes() The minimum number of times .
- AnyTimes() Any number of times ( Include 0 Time ).
Specify the call order :
// order of appointment gomock.InOrder( m.EXPECT().FunctionName("1"), m.EXPECT().FunctionName("2"), m.EXPECT().FunctionName("3"),)Be careful :
On the interface mock after , You need to pass in the mock After the object . If the test function cannot be passed in mock After the object , Cannot test .
func GetFromDB(key string) int { db := NewDB() if value, err := db.Get(key); err == nil { return value } return -1}// If this is the way , Unable to transfer in mock After db, Therefore, this method cannot test 3. Global variable piling
Stub: Global variables
GoStub It is also a pile driving tool in unit test , It supports global variables 、 Functions, etc . Generally, it is only used in unit tests to pile global variables ,Stub It is not convenient to pile a function . In addition, global variables can also be used gomonkey Pile driving .
Examples of use :
func TestGetConfig(t *testing.T) { // Is a global variable configFile Pile driving , Assign it a specified string stubs := gostub.Stub(&configFile, "./test.txt") defer stubs.Reset() // Execute the function to be tested data, err := GetConfig() if err != nil { t.Fatal() } // This function GetConfig() Global variables used in configFile It is the value set above }4. Functions
Monkey: function 、 Method
( because license problem , It is recommended to use more powerful and constantly updated gomonkey Package substitution )
monkey It's a Go Pile driving tools commonly used in unit tests , Its principle can be referred to this article Monkey Patching in Go: translation .
Gomonkey: function 、 Method 、 Variable
gomonkey is a library to make monkey patching in unit tests easy, and the core idea of monkey patching comes from Bouke, you can read this blogpost for an explanation on how it works.
gomonkey The principle of pile driving and bou.ke Of monkey It's the same , also gomonkey More features : Functions are supported 、 Member method 、 Global variables 、 Interface piling, etc . The following is a new version of github.com/agiledragon/gomonkey/v2 For example , Introduce different piling methods
function :ApplyFunc
// ApplyFunc( Parameters : The function to be driven ; Define a new function and its output )patches := ApplyFunc(myPackage.MyFunction, func(_ string, _ ...string) (string, error) { return "expected result", nil})defer patches.Reset()// Subsequent execution of the driven function , The result of the function is "expected result", nilres, err := myPackage.MyFunction("any string input", "any string input")Member method :ApplyMethodFunc
instance := &myStruct{}var p *myStruct// ApplyMethodFunc( Parameters : A pointer to a member ; Member method name ; Define the replacement function )patches := ApplyMethodFunc(p, "TheMethodName", func(_ int) error { return nil})defer patches.Reset()// The method of any real column of the member will be called later , Only the piling functions defined above are executed , The result returned to nilerr := instance.TheMethodName(1222)Global variables :ApplyGlobalVar
// ApplyGlobalVar( Parameters : Variable address , Value of variable piling )patches := ApplyGlobalVar(&num, 150)defer patches.Reset()Interface : It is necessary to combine and reuse the above ApplyFunc and ApplyMethod
( Piling of interfaces ,gomonkey It still doesn't have the above mock natural )
// Use ApplyFunc Point the function that returns the interface to an implementation object patches := ApplyFunc(TheFunctionReturnInterface, func(_ string) myInterface { return interfaceInstance})defer patches.Reset()// And then through ApplyMethod Change the behavior of the above implementation objects patches.ApplyMethod(interfaceInstance, "TheMethodName",func(_ *xxx, _ string) (string, error) { return "expected result", nil})Other types of piling may refer to gomonkey Warehouse example
5.HTTP Pile driving
httptest:
Server simulation
// simulation http serverfunc NewLocalHTTPSTestServer(handler http.Handler) (*httptest.Server, error) { ts := httptest.NewUnstartedServer(handler) cert, err := tls.LoadX509KeyPair("server.crt", "server.key") if err != nil { return nil, err } ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}} ts.StartTLS() // Start here https service , If it is ts.Start() It is HTTP service return ts, nil}// client Call tests func TestLocalHTTPSserver(t *testing.T) { handlerFunc := http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, client") // The processing logic for requests } ) ts, err := NewLocalHTTPSTestServer(handlerFunc) assert.Nil(t, err) defer ts.Close() // ts.URL Simulate for the above server Access address of , It can be done by replacing listener change res, err := http.Get(ts.URL) assert.Nil(t, err) // If you want to replace url: // newly build server Do not start first , That is to get rid of ts.StartTLS() // mylistener, err := net.Listen("tcp", "127.0.0.1:8080") // ts.Listener.Close() // ts.Listener = mylistener // ts.StartTLS() greeting, err := ioutil.ReadAll(res.Body) res.Body.Close() assert.Nil(t, err) assert.Equal(t, "Hello, client", string(greeting))}边栏推荐
- 嵌入式开发:硬件在环测试
- Dynamic feedback load balancing strategy based on Cluster
- Sixty years of deep learning
- win32版俄罗斯方块(学习MFC必不可少)
- Uncover the practice of Baidu intelligent test in the field of automatic test execution
- Five years after graduation, I asked all the leaders around me and summarized their learning methods
- College girls wear cheongsam to defend! Netizen: the tutor said it would be nice if the paper were as beautiful as the cheongsam
- 【云驻共创】通过Rust语言计算加速技术突破图片识别性能瓶颈
- golang7_TCP编程
- 【毕业季】这四年一路走来都很值得——老学长の忠告
猜你喜欢

CVPR上百人中招新冠,emoji成“呈堂证供”,M2 MBP被曝硬盘降速,今日更多大新闻在此...

Windbg常用命令详解

The imshow function of Matplotlib displays grayscale images. Vmin and vmax2 parameters should be set

昨天面试居然聊了半个多小时的异常处理

打造一个 API 快速开发平台,牛逼!

ANSVC无功补偿装置在河北某购物广场中的应用

WinDbg common commands

云原生(三十一) | Kubernetes篇之Kubernetes平台基本预装资源
![[system design] proximity service](/img/3b/b0dbd25945f9c914b097140890d8f9.png)
[system design] proximity service

直觉与实现:Batch Normalization
随机推荐
mysql多表查询
Gee - American landfire fire fire data set
Uncover the secret! Pay attention to those machines under the membership system!
Matlab fmincon precision, fmincon and quadprog error
Write it down once Net analysis of a property management background service stuck
Intuition and Implementation: batch normalization
高校女生穿旗袍答辩!网友:导师说论文要是和旗袍一样漂亮就好了
windows平台下的mysql启动等基本操作
Equivalence class partition method for test case design method
Horizon development board configuration network segment
Korean AI team plagiarizes the shock academic world! One tutor with 51 students, or plagiarism recidivist
Don't build the wheel again. It is recommended to use Google guava open source tool class library. It is really powerful!
Why is the integration of storage and computing a new direction of core making Collision school x Zhicun Technology
PHP FPM startup parameters and important configuration details
[document translation] camouflaged object detection
二叉树习题总结
3个最佳实践助力企业改善供应链安全
测试用例设计方法之等价类划分方法
Exploring the way of automated testing - Preparation
ES6 array method