This is a creation in Article, where the information may have evolved or changed.
After writing a Web server with the gin framework, if we need to test the handlers interface function, there are two main ways to do it.
The first is to deploy Web server and then manually impersonate a real HTTP request through a browser or other HTTP request emulation tool, and after sending an HTTP request, parsing the returned response to see if the response is expected, which is cumbersome and the test results are unreliable.
The second is to implement unit tests for handlers interface functions using httptest combined with testing.
Here are some of the ways to share unit tests based on gin with the corresponding unit tests for four interfaces:
Interface Name |
Request Address |
Request type |
Response data type |
Response data |
Ongetstringrequest |
/getstring |
Get |
String |
Success |
Onpracticerequest |
/practice |
Get |
HTML page |
Practice.html |
Onloginrequestforform |
/loginform |
Post |
Json |
|
Onloginrequestforjson |
/loginjson |
Post |
Json |
|
One, sample interface code:
ongetstringrequest :
//Ongetstringrequest return interface for Success strings ongetstringrequest (c *gin. Context) {c.string (http. Statusok"Success")}
onpracticerequest :
//Onpracticerequest return practice.html interface of the page onpracticerequest (c *gin. Context) {c.html (http. Statusok,"practice.html", gin. h{})}
Onloginrequestforform :
//Onloginrequestforform login interface to pass parameters as a formfuncOnloginrequestforform (c *gin. Context) {req: = &user{}ifERR: = C.shouldbindwith (req, binding. Form); Err! = Nil {log. Printf ("Err:%v", err) C.json (http.Statusok, Gin. h{"errno":"1","ErrMsg":"parameters do not match","Data":"", })return} C.json (http.Statusok, Gin. h{"errno":"0","ErrMsg":"","Data": Req,})}
Onloginrequestforjson :
//Onloginrequestforjson tothe login interface for passing parameters in JSON formfuncOnloginrequestforjson (c *gin. Context) {req: = &user{}ifERR: = C.shouldbindwith (req, binding. JSON); Err! = Nil {log. Printf ("Err:%v", err) C.json (http.Statusok, Gin. h{"errno":"1","ErrMsg":"parameters do not match","Data":"", })return} C.json (http.Statusok, Gin. h{"errno":"0","ErrMsg":"","Data": Req,})}
Second, some of the structure and tool functions called:
User struct Code:
//To undertake the front-end transmission over JSON data or Form form Data ' form: ' Age ' json: ' Age ' binding: ' Required '}
Loginresponse struct Code:
Response parameters ' JSON: ' Data ' 'loginresponse login interface
Call the tool function:
//PARSETOSTR will The key value pairs in the map are output as QueryString Form mapvalues}
Three, unit test writing steps:
1. Initialize the route
func init () {
// Initializing routes
router = gin. Default ()
Router. GET ("/getstring", Ongetstringrequest)
Router. POST ("/loginform", Onloginrequestforform)
Router. POST ("/loginjson", Onloginrequestforjson)
Router. Loadhtmlglob ("e:/mygo/resources/pages/*") // define template file path
router. GET ("/practice", Onpracticerequest)
}
When an interface involves operations related to a database, the database service can be added as middleware to the gin context as shown in:
2. Wrapping the function that constructs the HTTP request (so that the test function calls directly to initiate different kinds of HTTP requests)
2.1 Constructing a GET request
//Get based on a specific request URI, initiating GET request Returns response //Construction req: = httptest. Newrequest ("GET"//Call the corresponding// read response Body,_: = Ioutil. ReadAll (result. Body) Body}
2.2 Constructing a POST request, passing parameters as a form
//Postform based on a specific requestURI and Parametersparam, passing parameters in form form, initiatingPOST request return responsefuncPostform (URI string, paramMap[String]string, Router *gin. Engine) []byte {//ConstructionPOST request, form data tothe form of QueryString is added toafter the URIReq: = Httptest. Newrequest ("POST", Uri+parsetostr (param), nil)//Initialize responseW: = Httptest. Newrecorder ()//Call the appropriateHandler InterfaceRouter. Servehttp (W, req)//Extract ResponseResult: = W.result ()deferResult. Body.close ()//Read responseBodyBody, _: = Ioutil. ReadAll (result. Body)returnBody
2.3 Constructing a POST request, passing parameters in JSON form
//Postjson based on a specific requestURI and Parametersparam, topass parameters in JSON form, initiatingPOST request return responsefuncPostjson (URI string, paramMap[String]Interface{}, Router *gin. Engine) []byte {//Convert parameters toJSON bit streamJsonbyte,_: = json. Marshal (param)//ConstructionPOST request,JSON data to requestthe form of the body passesReq: = Httptest. Newrequest ("POST", Uri, Bytes. Newreader (Jsonbyte))//Initialize responseW: = Httptest. Newrecorder ()//Call the appropriateHandler InterfaceRouter. Servehttp (W, req)//Extract ResponseResult: = W.result ()deferResult. Body.close ()//Read responseBodyBody,_: = Ioutil. ReadAll (result. Body)returnBody
3. Writing test functions
3.1 Test functions for the Ongetstringrequest interface
//Testongetstringrequest test to gets the interface of a string to get the way //Initiator Body: = Get (URI, Router) fmt. Printf ("response:%v\n"{T.errorf ("response string does not match,body:%v\n" ,string (Body))}}
3.2 Test functions for the Onpracticerequest interface
//Testonpracticerequest test toGet method Getpractice.html interface of the pagefuncTestonpracticerequest (t *testing. T) {//Initialize request addressURI: ="/practice"//InitiatingGET RequestBody: = Get (URI, router) fmt. Printf ("response:%v\n", string (body))//Determine if the response is consistent with expectationsHtml,_: = Ioutil. ReadFile ("E:/mygo/resources/pages/practice.html") Htmlstr: = string (HTML)ifHtmlstr! = String (body) {T.errorf ("response data does not match,body:%v\n ", string (Body)}}
3.3 Test functions for the Onloginrequestforform interface
//Testonloginrequestforform Test Login interface to pass parameters in form formfuncTestonloginrequestforform (t *testing. T) {//Initialize request address and request parametersURI: ="/loginform"Param: = Make (Map[String]string] param["username"] ="Valiben"param["Password"] ="123"param["Age"] ="1"//InitiatingPOST request, passing parameters as a formBody: = Postform (URI, param, router) fmt. Printf ("response:%v\n", string (body))//Parse response to determine if the response is consistent with expectationsResponse: = &loginresponse{}ifERR: = json. Unmarshal (body, response); Err! = Nil {T.errorf ("Parse response error,err:%v\n ", err)}offResponse. Data.username! ="Valiben"{T.errorf ("response data does not match,username:%v\n ", Response. Data.username)}}
3.4 Test functions for the Onloginrequestforjson interface
//Testonloginrequestforjson test tothe login interface for passing parameters in JSON formfuncTestonloginrequestforjson (t *testing. T) {//Initialize request address and request parametersURI: ="/loginjson"Param: = Make (Map[String]Interface{}) param["username"] ="Valiben"param["Password"] ="123"param["Age"] = 1//InitiatingThe POST request topassing parameters in JSON formBody: = Postjson (URI, param, router) fmt. Printf ("response:%v\n", string (body))//Parse response to determine if the response is consistent with expectationsResponse: = &loginresponse{}ifERR: = json. Unmarshal (body, response); Err! = Nil {T.errorf ("Parse response error,err:%v\n ", err)}ifResponse. Data.username! ="Valiben"{T.errorf ("response data does not match,username:%v\n ", Response. Data.username)}}
4. Run unit tests to view test results
Execute go test./Run testing code, test results are as follows
Iv. Summary
The main step of unit testing based on gin is to initialize the route first, set the HTTP request address that the handler interface function intercepts (no listener port number), and then through the "Net/http/httptest" Packet Newrequest (method, Target string, body IO. Reader) method constructs the request, the first parameter is the requested type "POST" "GET", and the second parameter is the requested URI address (the parameters of form form can be passed in the form of QueryString appended to the URI address), The third parameter is the request Request body content (JSON data and other types of data can be added here), and then through the Newrecorder () function constructs the response, called the Func (engine *engine) servehttp (w http. Responsewriter, req *http. Request) method to invoke the interface handlers, the returned response will be written to the previously constructed response, and the interface can be tested by parsing the response and viewing the data