美文网首页Golang语言社区
go语言的单元测试

go语言的单元测试

作者: 吃猫的鱼0 | 来源:发表于2018-01-03 10:45 被阅读0次

简单单元测试

直接上栗子吧

main.go

package 测试


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

main_test.go

package 测试

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")
    }
}

然后我们在终端的项目目录下运行go test -v就可以看到测试结果了。

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

D:\go\workspace\src\测试>

注:

  • 含有单元测试代码的go文件必须以_test.go结尾,Go语言测试工具只认符合这个规则的文件
  • 单元测试文件名_test.go前面的部分最好是被测试的方法所在go文件的文件名,比如例子中是main_test.go,因为测试的Add函数,在main.go文件里
  • 单元测试的函数名必须以Test开头,是可导出公开的函数
  • 测试函数的签名必须接收一个指向testing.T类型的指针,并且不能返回任何值
  • 函数名最好是Test+要测试的方法函数名,比如例子中是TestAdd,表示测试的是Add这个这个函数

表组测试

这个和基本的单元测试非常相似,只不过它是有好几个不同的输入以及输出组成的一组单元测试。

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")
        }
}

模拟调用

单元测试的原则,就是你所测试的函数方法,不要受到所依赖环境的影响,比如网络访问等,因为有时候我们运行单元测试的时候,并没有联网,那么总不能让单元测试因为这个失败吧?所以这时候模拟网络访问就有必要了。
针对模拟网络访问,标准库了提供了一个httptest包,可以让我们模拟http的网络调用,下面举个例子了解使用。

创建http请求的函数

首先我们创建一个处理HTTP请求的函数,并注册路由

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)
}

非常简单,这里是一个/sendjsonAPI,当我们访问这个API时,会返回一个JSON字符串。现在我们对这个API服务进行测试,但是我们又不能时时刻刻都启动着服务,所以这里就用到了外部终端对API的网络访问请求。

func init()  {
        common.Routes()
}
func TestSendJSON(t *testing.T){
    //创建一个请求
    req, err := http.NewRequest("GET", "/health-check", nil)
    if err != nil {
        t.Fatal(err)
    }
    data:=url.Values{}
    data.Set("code","123")
    req.Form=data
    // 我们创建一个 ResponseRecorder (which satisfies http.ResponseWriter)来记录响应
    rr := httptest.NewRecorder()
    requester := network.Requester{From: network.MiniServiceHallWeb}
    //直接使用HealthCheckHandler,传入参数rr,req
    MyHandler(requester,rr, req)

    // 检测返回的状态码
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // 检测返回的数据
    expected := `{"alive": true}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

在测试机上模拟一个服务器

func mockServer() *httptest.Server {
        //API调用处理函数
        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)
        }
        //适配器转换
        return httptest.NewServer(http.HandlerFunc(sendJson))
}
func TestSendJSON(t *testing.T) {
        //创建一个模拟的服务器
        server := mockServer()
        defer server.Close()
        //Get请求发往模拟服务器的地址
        resq, err := http.Get(server.URL)
        if err != nil {
                t.Fatal("创建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)
}

测试覆盖率

我们尽可能的模拟更多的场景来测试我们代码的不同情况,但是有时候的确也有忘记测试的代码,这时候我们就需要测试覆盖率作为参考了。

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)
}

现在我们使用go test工具运行单元测试,和前几次不一样的是,我们要显示测试覆盖率,所以要多加一个参数-coverprofile,所以完整的命令为:go test -v -coverprofile=c.out,-coverprofile是指定生成的覆盖率文件,例子中是c.out,这个文件一会我们会用到。现在我们看终端输出,已经有了一个覆盖率。

=== 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%的测试覆盖率,还没有到100%,那么我们看看还有那些代码没有被测试到。这就需要我们刚刚生成的测试覆盖率文件c.out生成测试覆盖率报告了。生成报告有go为我们提供的工具,使用go tool cover -html=c.out -o=tag.html,即可生成一个名字为tag.html的HTML格式的测试覆盖率报告,这里有详细的信息告诉我们哪一行代码测试到了,哪一行代码没有测试到。

从上图中可以看到,标记为绿色的代码行已经被测试了;标记为红色的还没有测试到,有2行的,现在我们根据没有测试到的代码逻辑,完善我的单元测试代码即可。

相关文章

  • go 单元测试

    单元测试 Go 语言测试框架可以让我们很容易地进行单元测试,但是需要遵循五点规则: 含有单元测试代码的 go 文件...

  • Go 语言 Unit Testing 单元测试

    关于 Go 的基本语法,参见:半天时间 Go 语言的基本实践 单元测试 Go 中提供了 testing 这个 pa...

  • Go单元测试(一):基本用法

    来自公众号:灰子学技术 原文链接 一、单元测试的基本规则介绍 Go的单元测试比较容易实现,因为Go语言为我们提供了...

  • golang 单元测试 UnitTest 覆盖率 基准测试

    [TOC] 单元测试要求 因为golang语言设计,偏向工程性,故go 单元测试对文件名和方法名,参数都有很严格的...

  • Go语言单元测试

    1. 概述 单元测试(unit testing) 是指对软件 中的 最小可测试单元进行检查和验证Go语言自身提供...

  • 005-golang-单元测试

    golang的单元测试 Go语言中自带有一个轻量级的测试框架testing和自带的 go test命令来实现单元测...

  • go语言的单元测试

    Go语言中自带有一个轻量级的测试框架testing和自带的go test命令来实现单元测试和性能测试,testin...

  • golang 单元测试(gotests、mockery自动生成)

    golang 单元测试 文件格式:go单元测试,有固定的名称格式,所有以_test.go为后缀名的源文件在执行go...

  • go test 单元测试

    go test 单元测试 文件格式:go单元测试,有固定的名称格式,所有以_test.go为后缀名的源文件在执行g...

  • go test

    参考 一步步教你编写可测试的Go语言代码 示例 单元测试规则: func TestXxx (t *testing....

网友评论

    本文标题:go语言的单元测试

    本文链接:https://www.haomeiwen.com/subject/gpzlnxtx.html