This is a creation in Article, where the information may have evolved or changed.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
What is unit testing
Believe that we are programmers, the unit test is not unfamiliar. Unit tests are generally used to test our code logic with no problems and there is no running as we expect to ensure code quality.
Most unit tests are tests of a function method to ensure that no problems or problems can be predicted by us as much as possible. To achieve this, we can use a variety of means, logic, simulation of different scenarios for testing.
Here we package main
define a function Add
to find the sum of two numbers, and then we use the unit test for the summation logic test.
Main.go
123 |
func ADD int) int {return a+b} |
Main_test.go
12345678 |
func Testadd (t *testing. T) {sum: = Add (1,2)if3 {t.Log ("The result is OK"Else {t.fatal ("The result is Wrong")}} |
Then we can see the test results by running in the project directory of the terminal go test -v
.
123456 |
➜ Test -v=== RUN testadd---pass:testadd (0.00s) main_test.go:26:the result is Okpassok Flysnow.org/hello 0.007s |
There are test success pass marks and print out the results we want. More about go test
the usage of the previous written go development tool http://www.flysnow.org/2017/03/08/go-in-action-go-tools.html, here do not elaborate.
The go language provides us with a test framework to help us make unit testing easier, but using this framework requires the following rules:
- The go file containing the unit test code must
_test.go
end, and the Go Language test tool will only recognize files that match this rule
- The first part of the unit test file name is
_test.go
preferably the file name of the go file where the method being tested, as in the example main_test.go
, because the test Add
function, in the main.go
file
- The function name of the unit test must
Test
begin with a function that can be exported publicly
- The signature of the test function must receive a
testing.T
pointer to the type and cannot return any values
- The function name is best test+ the method function name to be tested, as in the example, this is the
TestAdd
function that represents the test. Add
Following the above rules, we can easily write unit tests, the focus of unit testing is to test the logic of the code, scenarios, so as to test comprehensively, to ensure the code quality logic.
Table Group Test
There is also a unit test method called a table group test, which is very similar to the basic unit test, except that it is a set of unit tests consisting of several different inputs and outputs.
For example, in the last example, we tested 1+2
, if we add 3+4
, and so on 9+2
, there are several inputs, and there are several output, this one-time test many of the input and output scenarios of the test, is the table group test.
123456789101112131415 |
func Testadd (t *testing. T) {sum: = Add (1,2)if3 {t.Log ("The result is OK"Else {t.fatal ("The result is wrong")}sum=add (3,4)if7 {T. Log ("The result is OK"orelse {t.fatal ("Theresult is wrong")}} |
Impersonation Call
Unit testing is the principle of the function you are testing, not affected by the environment, such as network access, because sometimes when we run unit testing, and there is no network, then can not let the unit test because of this failure? So it is necessary to simulate network access at this time.
For analog network access, the standard library provides a httptest package that allows us to simulate the network invocation of HTTP, as an example of how to use it.
First we create a function to process the HTTP request and register the route
12345678910111213141516171819202122 |
package commonimport ( "Net/http" "Encoding/json" ) func Span class= "title" >routes () {http. Handlefunc ( "/sendjson" , Sendjson)}func sendjson (rw http. Responsewriter,r *http. Request) {u: = struct {Name string }{name:< Span class= "string" > "Zhang San" ,}RW. Header (). Set ( "Content-type" , ) rw. Writeheader (http. Statusok) JSON. Newencoder (rw). Encode (U)} |
The
is very simple, here is a /sendjson
API, and when we access this API, we return a JSON string. Now we're testing this API service, but we can't start the service all the time, so we're going to use the external terminal's network access request to the API.
1234567891011121314151617 |
func init Span class= "params" > () {Common. Routes ()}func testsendjson (t *testing. T) {req,err:=http. Newrequest (http. Methodget, "/sendjson" , nil ) if err!=nil {t.fatal ()}rw:=httptest. Newrecorder () http. Defaultservemux.servehttp (rw,req) log. Println ( "code:" , RW. Code) log. Println (, RW. Body.string ())} |
Running this unit test, you can see the results of our access to the /sendjson
API, and we did not start any HTTP service to achieve the goal. The main use of this is httptest.NewRecorder()
to create a response http.ResponseWriter
that simulates the real service side, which is triggered by the calling http.DefaultServeMux.ServeHTTP
method.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.
There is also a way to simulate a call, which is to actually impersonate a server on the test machine and then make the call test.
1234567891011121314151617181920212223242526272829303132333435 |
func mockserver() *httptest. Server {//api Call handler functionSendjson: = func(rw http. Responsewriter, R *http. Request) {u: =struct{Namestring}{name:"Zhang San",}RW. Header (). Set ("Content-type","Application/json") RW. Writeheader (http. Statusok) JSON. Newencoder (rw). Encode (U)}//Adapter ConversionreturnHttptest. NewServer (http. Handlerfunc (Sendjson))} func testsendjson(t *testing. T) {//Create a simulated serverServer: = Mockserver ()deferServer. Close ()//get the address of the request destined to the analog serverResq, Err: = http. Get (server. URL)ifErr! =Nil{T.fatal ("Create get Failed")}deferResq. Body.close () log. Println ("Code:", Resq. StatusCode) JSON, err: = Ioutil. ReadAll (Resq. Body)ifErr! =Nil{log. Fatal (Err)}log. Printf ("body:%s\n", JSON)} |
The simulation server is created using a httptest.NewServer
function that receives an http.Handler
interface that processes API requests.
The code example uses the hander adapter pattern, http.HandlerFunc
is a function type, implements the http.Handler
interface, here is the coercion type conversion, is not a function call, more information about this adapter, you can refer to a previous article I wrote: interface type function,/HTTP Www.flysnow.org/2016/12/30/golang-function-interface.html.
This creates the analog server, listens to the native IP 127.0.0.1
, the port is random. Then we send a GET request, no longer sent /sendjson
to, but to simulate the server's address server.URL
, the rest is the same as the access to the normal URL, print out the results.
Test coverage
We try to simulate more scenarios to test the different situations of our code, but sometimes we do have the code to forget the test, and we need to test coverage as a reference.
The code of the Unit test, which triggers the number of lines of code in the code to be run to, is called test coverage, and code coverage is not necessarily exact, but can be used as a reference to help us measure the gap between our projected coverage, go test
tools, Provides us with the ability to measure test coverage.
Main.go
12345678910111213 |
func Tag int) {Switch tag {case1: FMT. Println ("Android")case2: FMT. Println ("Go")case3: FMT. Println ("Java")default: FMT. Println ("C")}} |
Main_test.go
12345 |
func Testtag (t *testing. T) {tag (1) tag (2)} |
Now we use the go test
tool to run unit tests, unlike the previous few times, we want to show the test coverage, so we need to add a parameter -coverprofile
, so the complete command go test -v -coverprofile=c.out
is:, is the -coverprofile
specified generated coverage file, in the example c.out
, We'll use this file in a minute. Now we look at the terminal output, which already has a coverage.
1234567 |
= = = RUN testtagandroidgo---pass:testtag (0.00s) passcoverage:60.0% of Statementsok Flysnow.org/hello 0.005s |
coverage: 60.0% of statements
, 60% of the test coverage is not yet up to 100%, so let's see if the code has not been tested. This requires that the test coverage file we just generated c.out
generate a test coverage report. The build report has the tools that go provides for us, and we go tool cover -html=c.out -o=tag.html
can generate a tag.html
test coverage report with the name of the HTML format, with detailed information that tells us which line of code was tested and which line of code was not tested.
As you can see, the lines of code marked green have been tested, the red ones have not been tested, there are 2 rows, and now we can refine my unit test code based on the code logic that is not tested.
1234567 |
func Testtag (t *testing. T) {tag (1) tag (2) tag (3) tag (6)} |
Unit test perfect as above code, and then run the unit test, you can see the test coverage is already 100%, done.
"Go language Combat" reading notes, not to be continued, welcome to sweep code attention flysnow_org
to the public or website http://www.flysnow.org/, the first time to see follow-up notes. If you feel helpful, share it with your friends and thank you for your support.