go語(yǔ)言的單元測(cè)試

簡(jiǎn)單單元測(cè)試

直接上栗子吧

main.go

package 測(cè)試


func Add(a,b int) int{
    return a+b
}

main_test.go

package 測(cè)試

import "testing"

func TestAdd(t *testing.T) {
    sum := Add(1,2)
    if sum == 3 {
        t.Log("the result is ok")
    } else {
        t.Fatal("the result is wrong")
    }
}

然后我們?cè)诮K端的項(xiàng)目目錄下運(yùn)行g(shù)o test -v就可以看到測(cè)試結(jié)果了。

D:\go\workspace\src\測(cè)試>go test -v
=== RUN   TestAdd
=--- PASS: TestAdd (0.00s)
        main_test.go:8: the result is ok
PASS
ok      測(cè)試    0.266s

D:\go\workspace\src\測(cè)試>

注:

  • 含有單元測(cè)試代碼的go文件必須以_test.go結(jié)尾,Go語(yǔ)言測(cè)試工具只認(rèn)符合這個(gè)規(guī)則的文件
  • 單元測(cè)試文件名_test.go前面的部分最好是被測(cè)試的方法所在go文件的文件名,比如例子中是main_test.go,因?yàn)闇y(cè)試的Add函數(shù),在main.go文件里
  • 單元測(cè)試的函數(shù)名必須以Test開(kāi)頭,是可導(dǎo)出公開(kāi)的函數(shù)
  • 測(cè)試函數(shù)的簽名必須接收一個(gè)指向testing.T類型的指針,并且不能返回任何值
  • 函數(shù)名最好是Test+要測(cè)試的方法函數(shù)名,比如例子中是TestAdd,表示測(cè)試的是Add這個(gè)這個(gè)函數(shù)

表組測(cè)試

這個(gè)和基本的單元測(cè)試非常相似,只不過(guò)它是有好幾個(gè)不同的輸入以及輸出組成的一組單元測(cè)試。

func TestAdd(t *testing.T) {
        sum := Add(1,2)
        if sum == 3 {
                t.Log("the result is ok")
        } else {
                t.Fatal("the result is wrong")
        }
        
        sum=Add(3,4)
        if sum == 7 {
                t.Log("the result is ok")
        } else {
                t.Fatal("the result is wrong")
        }
}

模擬調(diào)用

單元測(cè)試的原則,就是你所測(cè)試的函數(shù)方法,不要受到所依賴環(huán)境的影響,比如網(wǎng)絡(luò)訪問(wèn)等,因?yàn)橛袝r(shí)候我們運(yùn)行單元測(cè)試的時(shí)候,并沒(méi)有聯(lián)網(wǎng),那么總不能讓單元測(cè)試因?yàn)檫@個(gè)失敗吧?所以這時(shí)候模擬網(wǎng)絡(luò)訪問(wèn)就有必要了。
針對(duì)模擬網(wǎng)絡(luò)訪問(wèn),標(biāo)準(zhǔn)庫(kù)了提供了一個(gè)httptest包,可以讓我們模擬http的網(wǎng)絡(luò)調(diào)用,下面舉個(gè)例子了解使用。

創(chuàng)建http請(qǐng)求的函數(shù)

首先我們創(chuàng)建一個(gè)處理HTTP請(qǐng)求的函數(shù),并注冊(cè)路由

package common
import (
        "net/http"
        "encoding/json"
)
func Routes(){
        http.HandleFunc("/sendjson",SendJSON)
}
func SendJSON(rw http.ResponseWriter,r *http.Request){
        u := struct {
                Name string
        }{
                Name:"張三",
        }
        rw.Header().Set("Content-Type","application/json")
        rw.WriteHeader(http.StatusOK)
        json.NewEncoder(rw).Encode(u)
}

非常簡(jiǎn)單,這里是一個(gè)/sendjsonAPI,當(dāng)我們?cè)L問(wèn)這個(gè)API時(shí),會(huì)返回一個(gè)JSON字符串。現(xiàn)在我們對(duì)這個(gè)API服務(wù)進(jìn)行測(cè)試,但是我們又不能時(shí)時(shí)刻刻都啟動(dòng)著服務(wù),所以這里就用到了外部終端對(duì)API的網(wǎng)絡(luò)訪問(wèn)請(qǐng)求。

func init()  {
        common.Routes()
}
func TestSendJSON(t *testing.T){
    //創(chuàng)建一個(gè)請(qǐng)求
    req, err := http.NewRequest("GET", "/health-check", nil)
    if err != nil {
        t.Fatal(err)
    }
    data:=url.Values{}
    data.Set("code","123")
    req.Form=data
    // 我們創(chuàng)建一個(gè) ResponseRecorder (which satisfies http.ResponseWriter)來(lái)記錄響應(yīng)
    rr := httptest.NewRecorder()
    requester := network.Requester{From: network.MiniServiceHallWeb}
    //直接使用HealthCheckHandler,傳入?yún)?shù)rr,req
    MyHandler(requester,rr, req)

    // 檢測(cè)返回的狀態(tài)碼
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // 檢測(cè)返回的數(shù)據(jù)
    expected := `{"alive": true}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

在測(cè)試機(jī)上模擬一個(gè)服務(wù)器

func mockServer() *httptest.Server {
        //API調(diào)用處理函數(shù)
        sendJson := func(rw http.ResponseWriter, r *http.Request) {
                u := struct {
                        Name string
                }{
                        Name: "張三",
                }
                rw.Header().Set("Content-Type", "application/json")
                rw.WriteHeader(http.StatusOK)
                json.NewEncoder(rw).Encode(u)
        }
        //適配器轉(zhuǎn)換
        return httptest.NewServer(http.HandlerFunc(sendJson))
}
func TestSendJSON(t *testing.T) {
        //創(chuàng)建一個(gè)模擬的服務(wù)器
        server := mockServer()
        defer server.Close()
        //Get請(qǐng)求發(fā)往模擬服務(wù)器的地址
        resq, err := http.Get(server.URL)
        if err != nil {
                t.Fatal("創(chuàng)建Get失敗")
        }
        defer resq.Body.Close()
        log.Println("code:", resq.StatusCode)
        json, err := ioutil.ReadAll(resq.Body)
        if err != nil {
                log.Fatal(err)
        }
        log.Printf("body:%s\n", json)
}

測(cè)試覆蓋率

我們盡可能的模擬更多的場(chǎng)景來(lái)測(cè)試我們代碼的不同情況,但是有時(shí)候的確也有忘記測(cè)試的代碼,這時(shí)候我們就需要測(cè)試覆蓋率作為參考了。

main.go

func Tag(tag int){
        switch tag {
        case 1:
                fmt.Println("Android")
        case 2:
                fmt.Println("Go")
        case 3:
                fmt.Println("Java")
        default:
                fmt.Println("C")
        }
}

main_test.go

func TestTag(t *testing.T) {
        Tag(1)
        Tag(2)
}

現(xiàn)在我們使用go test工具運(yùn)行單元測(cè)試,和前幾次不一樣的是,我們要顯示測(cè)試覆蓋率,所以要多加一個(gè)參數(shù)-coverprofile,所以完整的命令為:go test -v -coverprofile=c.out,-coverprofile是指定生成的覆蓋率文件,例子中是c.out,這個(gè)文件一會(huì)我們會(huì)用到。現(xiàn)在我們看終端輸出,已經(jīng)有了一個(gè)覆蓋率。

=== RUN   TestTag
Android
Go
--- PASS: TestTag (0.00s)
PASS
coverage: 60.0% of statements
ok      flysnow.org/hello       0.005s

coverage: 60.0% of statements,60%的測(cè)試覆蓋率,還沒(méi)有到100%,那么我們看看還有那些代碼沒(méi)有被測(cè)試到。這就需要我們剛剛生成的測(cè)試覆蓋率文件c.out生成測(cè)試覆蓋率報(bào)告了。生成報(bào)告有g(shù)o為我們提供的工具,使用go tool cover -html=c.out -o=tag.html,即可生成一個(gè)名字為tag.html的HTML格式的測(cè)試覆蓋率報(bào)告,這里有詳細(xì)的信息告訴我們哪一行代碼測(cè)試到了,哪一行代碼沒(méi)有測(cè)試到。

從上圖中可以看到,標(biāo)記為綠色的代碼行已經(jīng)被測(cè)試了;標(biāo)記為紅色的還沒(méi)有測(cè)試到,有2行的,現(xiàn)在我們根據(jù)沒(méi)有測(cè)試到的代碼邏輯,完善我的單元測(cè)試代碼即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,692評(píng)論 19 139
  • Android單元測(cè)試介紹 處于高速迭代開(kāi)發(fā)中的Android項(xiàng)目往往需要除黑盒測(cè)試外更加可靠的質(zhì)量保障,這正是單...
    東經(jīng)315度閱讀 3,424評(píng)論 6 37
  • 單元測(cè)試不是一個(gè)小工程,需要多用些時(shí)間才能做好,不要希望通過(guò)這個(gè)文章就能掌握單元測(cè)試,這只是一個(gè)入門,需要自己動(dòng)手...
    勇不言棄92閱讀 8,108評(píng)論 9 60
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,323評(píng)論 25 708
  • 上一章節(jié)網(wǎng)店打雜 看著店里生意越來(lái)越好,每天下午發(fā)出的包裹數(shù)量越來(lái)越多,兩個(gè)人雖然每天累得跟狗似的,但是狀態(tài)卻是特...
    簡(jiǎn)小塵閱讀 473評(píng)論 0 2

友情鏈接更多精彩內(nèi)容