Go中的测试之单元测试

单元测试时用来测试包或者程序的一部分代码或者一组代码的函数。
在Go语言中,其单元测试主要包括:基础测试,其只使用一组参数和结果来测试一段代码。表组测试也会测试一段代码,但是会使用多组参数和结果进行测试。也可以使用一些方法来模拟测试代码需要用到的外部资源,如数据库或者网络服务器。这有助于让测试在没有所需的外部资源可用的时候,模拟这些资源的行为让测试正常进行。

1、基础单元测试

package chapter9

import (
	"net/http"
	"testing"
)

const checkMark  = "\u2713"
const ballotX  = "\u2717"

func TestDownload(t *testing.T)  {
	url := "http://www.goinggo.net/index.html"
	statusCode := 200

	t.Log("Given the need to test downloading content.")
	{
		t.Logf("\t When checking \"%s\" for status code \"%d\"", url, statusCode)
		{
			resp, err := http.Get(url)
			if err != nil {
				t.Fatal("\t\t Should be able to make the Get call.", ballotX, err)
			}
			t.Fatal("\t\t Should be able to make the Get call.", checkMark)
			defer resp.Body.Close()
			if resp.StatusCode == statusCode {
				t.Logf("\t\t Should receive a \"%d\" status. %v", statusCode, checkMark)
			} else {
				t.Errorf("\t\t Should reveive a \"%d\" status. %v %v",statusCode, ballotX, resp.StatusCode)
			}
		}
	}

}

通过运行命令 go test -v 来运行这个测试(-v 提供冗余输出)
测试文件要以 _test.go 命名结束,这主要是go语言的测试工具只会认以 _test.go 结尾的文件是测试文件。

2、表组测试

如果测试可以接受一组不同的输入并产生不同的输出代码,那么应该使用表组测试的方法进行测试。表组测试除了会有一组不同的输入值和期望结果之外,其余部分都很像基础单元测试。

package chapter9

import (
	"net/http"
	"testing"
)

const checkMark  = "\u2713"
const ballotX  = "\u2717"

func TestDownload(t *testing.T)  {
	var urls = []struct{
		url string
		statusCode int
	}{
		{
			"https://www.baidu.com/",
			http.StatusOK,
		},
		{
			"http://res.cnn.com/rss/cnn_topstabdurl.rss",
			http.StatusNotFound,
		},
	}

	t.Log("Given the need to test downloading different content")
	{
		for _, u := range urls{
			t.Logf("\tWhen checking \"%s\" for status code \"%d\"", u.url, u.statusCode)
			{
				resp, err := http.Get(u.url)
				if err != nil {
					t.Fatal("\t\t Should be able to Get the url.", ballotX, err)
				}
				t.Log("\t\t Should be able to Get the url", checkMark)

				defer resp.Body.Close()

				if resp.StatusCode == u.statusCode {
					t.Logf("\t\t Should have a \"%d\" status. %v",u.statusCode, checkMark)
				} else {
					t.Errorf("\t\t Should have a \"%d\" status %v %v", u.statusCode, ballotX, resp.StatusCode)
				}
			}
		}
	}


}

3、模仿调用

有些程序的测试可能需要连接互联网,才能保证测试运行成功。不能总是假设运行测试的机器可以访问互联网,如果突然断网,则必定影响到测试的进度。
为了解决这个问题,Go 的标准库中包含一个名为 httptest 的包,它让开发人员可以模仿基于HTTP 网络调用。模仿( mocking) 是一个很常用的技术手段,用来在测试时模拟访问不可用的资源。

package chapter9

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"testing"
)

const checkMark  = "\u2713"
const ballotX  = "\u2717"

// feed 模仿了我们期望接收的 XML 文档
var feed = `<?xml version="1.0" encoding="UTF-8"?>
<rss>
<channel>
    <title>Going Go Programming</title>
    <description>Golang : https://github.com/goinggo</description>
    <link>http://www.goinggo.net/</link>
    <item>
        <pubDate>Sun, 15 Mar 2015 15:04:00 +0000</pubDate>
        <title>Object Oriented Programming Mechanics</title>
        <description>Go is an object oriented language.</description>
        <link>http://www.goinggo.net/2015/03/object-oriented</link>
    </item>
</channel>
</rss>`

// mockServer 返回用来处理请求的服务器的指针
func mockServer() *httptest.Server  {
	f := func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(200)
		w.Header().Set("Content-Type", "application/xml")
		fmt.Fprintln(w, feed)
	}
	return httptest.NewServer(http.HandlerFunc(f))

}

func TestDownload(t *testing.T)  {
	statusCode := http.StatusOK

	server := mockServer()
	defer server.Close()

	t.Log("Given the need to test downloading content.")
	{
		t.Logf("\t When checking \"%s\" for status code \"%d\"", server.URL, statusCode)
		{
			resp, err := http.Get(server.URL)
			if err != nil{
				t.Fatal("\t\t Should be able to make the Get call.", ballotX, err)
			}
			t.Log("\t\t Should be able to make the Get call.", checkMark)
			defer resp.Body.Close()

			if resp.StatusCode != statusCode {
				t.Fatalf("\t\t Should receive a \"%d\" status. %v %v", statusCode, ballotX, resp.StatusCode)
			}
			t.Logf("\t\t Should receive a \"%d\" status. %v", statusCode, checkMark)
		}

	}
}

4、测试服务端点

服务端点是指与服务宿主信息无关,用来分辨某个服务的地址,一般是不包含宿主的一个路径。如果在构造网络API,你会希望直接测试自己的服务的所有服务端点,而不用启动整个网络服务。包 httptest 正好提供了做到这一点的机制。

// Package handlers provides the endpoints for the web service.
package handlers

import (
	"encoding/json"
	"net/http"
)

// Routes sets the routes for the web service.
func Routes() {
	http.HandleFunc("/sendjson", SendJSON)
}

// SendJSON returns a simple JSON document.
func SendJSON(rw http.ResponseWriter, r *http.Request) {
	u := struct {
		Name  string
		Email string
	}{
		Name:  "Bill",
		Email: "bill@ardanstudios.com",
	}

	rw.Header().Set("Content-Type", "application/json")
	rw.WriteHeader(200)
	json.NewEncoder(rw).Encode(&u)
}

package chapter9

import (
	"encoding/json"
	"myExtends/handlers"
	"net/http"
	"net/http/httptest"
	"testing"
)

const checkMark  = "\u2713"
const ballotX  = "\u2717"

func init()  {
	handlers.Routes()

}

func TestSendJSON(t *testing.T)  {
	t.Log("Given the need to test the SendJSON endpoint.")
	{
		req, err := http.NewRequest("GET", "/sendjson", nil)
		if err != nil {
			t.Fatal("\t Should be able to create a request.", ballotX, err)
		}
		t.Log("\t Should be able to create a request.",checkMark)
		rw := httptest.NewRecorder()
		http.DefaultServeMux.ServeHTTP(rw, req)
		if rw.Code != 200 {
			t.Fatal("\t Should receive \"200\"", ballotX, rw.Code)
		}
		t.Log("\tShould receive \"200\"", checkMark)

		u := struct {
			Name string
			Email string
		}{}
		if err := json.NewDecoder(rw.Body).Decode(&u); err != nil {
			t.Fatal("\t Should decode the response.", ballotX)
		}
		t.Log("\t Should decode the respons.", checkMark)
		if u.Name == "Bill"{
			t.Log("\t Should have a name.", checkMark)
		} else {
			t.Error("\t Should have a name.", ballotX, u.Name)
		}
		if u.Email == "bill@ardanstudios.com" {
			t.Log("\t Should have an Email.", checkMark)
		} else {
			t.Error("\t Should have an Email.", ballotX, u.Email)
		}
	}

}

从上面代码可知, http.NewRequest() 函数创建了一个 http.Request 值。这个 Request 值使用 GET 方法调用 /sendjson 服务端点的相应。由于这个调用使用的是 GET 方法,第三个发送数据的参数被传为 nil。
httptest.NewRecoder 函数创建了一个 http.ResponseRecorder 值。有了http.Request 和 http.ResponseRecorder 这两个值,就可以调用服务默认的多路选择器(mux)的 ServeHttp 方法。调用这个方法模仿了外部客户端对 /sendjson 服务端点的请求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值