Go testing 单元测试


可以使用: go test -v 在文件所在位置测试代码

go test -v

-v 提供 冗余输出: 如果不加 “-v” 除非测试失败,否则无法看到任何测试输出

Note

  1. go 语言的测试工具只会认为以 _test.go 结尾的文件是测试文件,一旦测试工具找到了测试文件,就会找到里面的测试函数并执行。
    注:如果没有遵循该约定,就不会有测试报告
  1. 一个测试函数必须是公开的函数,以Test开头, 并且函数的签名必须接受一个指向testing.T 类型的指针,并且不用返回任何值
    注:如果没有遵循该约定,测试框架将无法识别测试函数并执行

testing.T 类型指针

t *testing.T

t.Log: 输出测试的消息
t.Logf: 格式化消息

t.Fatal: 不单报告单元测试已经失败,而且会向测试输出写入一些消息,然后立刻停止这个测试函数的执行(如果还有其他的测试函数,会继续执行其他的测试函数)
t.Fatalf: t.Fatal 的格式换版本

t.Error: 报告测试的失败,但不会停止当前测试函数的执行
t.Errorf: t.Error 的格式化版本

1. 基础单元测试 (basic test)

Get 基础单元测试

// 基础单元测试
package basic_test

import (
    "net/http"
    // testing 包提供了从报告测试的输出和状态的各种测试功能
	"testing"
)

// 声明对号(√)
const checkMark = "\u2713"
// 声明叉号(x)
const ballotX = "\u2717"

// TestDownload 测试函数
// 一个测试函数必须是公开的函数,且以Test开头
// 函数的签名必须接受一个指向testing.T 类型的指针,并且不用返回任何值
func TestDownload(t *testing.T) {
    // 声明url 和 状态码
	url := "http://www.goinggo.net/feeds/posts/default?alt=rss"
	statusCode := 200
	t.Log("Given the needs to test downloading content.")
	{
		t.Logf("\tWhen 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.Log("\t\tShould be able to make the Get call.", checkMark)

			defer resp.Body.Close()

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

如果测试函数没有调用过 t.Fatal 或 t.Error 方法,就任务 测试通过了


2. 表组测试(table test)

表组测试可以接收一组不同的输入并产生不同的输出代码

// 表组测试
package table_test

import (
	"net/http"
	"testing"
)

// 声明对号(√)
const checkMark = "\u2713"
// 声明对号(x)
const ballotX = "\u2717"

// TestDownload 可以确认http包的Get函数可以下载的内容,并正确处理不同的状态
func TestDownload(t *testing.T) {
	var urls = []struct {
		url        string
		statusCode int
	}{
        // 配置两组数值(url, statusCode)
		{
			"http://www/goinggo.net/feeds/posts/defaults?alt=rss",
			http.StatusOK,
		},
		{
			"http://rss.cnn.com/rss/cnn_topstbadurl.rss",
			http.StatusNotFound,
		},
	}

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

				t.Log("\t\tShould be able to get the url.", checkMark)

                // 函数结束后,关闭请求体
				defer resp.Body.Close()

				if resp.StatusCode == u.statusCode {
					t.Logf("\t\tShould have a \"%d\" status. %v", u.statusCode, checkMark)

				} else {
					t.Errorf("\t\tShould have a \"%d\" status. %v %v", u.statusCode, ballotX, resp.StatusCode)
				}
			}
		}
	}
}

如果后续需要扩展测试,只需要将新的url和statusCode加入表组就好了


3. 模仿测试(mocking test)

标准库httptest包,可以让用户模仿HTTP的网络调用,用来运行测试时访问不可用的资源

// 模仿调用
package basic_test

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

// 声明对号(√)
const checkMark = "\u2713"
// 声明对号(x)
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>
		<pubData></pubData>
		<title>Object Oriented Programming Mechanics</title>
		<description>Go is a 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))
}

// TestDownloadT 确认http包的Get函数可以下载内容,并且内容可以反序列化并关闭
func TestDownloadT(t *testing.T) {
    statusCode := http.StatusOK
    // 调用mockServer函数生成模仿服务器
    server := mockServer()
    // 在函数返回时执行Close方法
	defer server.Close()

	t.Log("Given the need to test Download content.")
	{
		t.Logf("When checking \"%s\" for status code \"%d\"", server.URL, statusCode)
		{
            // server.URL 提供访问的url
			resp, err := http.Get(server.URL)
			if err != nil {
				t.Fatal("\t\tShould be able to make the Get call", ballotX, err)
			}
			t.Log("\t\tShould be albe to make the Get call.", checkMark)

			defer resp.Body.Close()
			if resp.StatusCode != statusCode {
				t.Fatalf("\t\tShould received a \"%d\" status. %v %v", statusCode, ballotX, resp.StatusCode)
			}

			t.Logf("\t\tShould received a \"%d\" status. %v", statusCode, checkMark)

		}
	}
}

4. 测试服务端点

文件目录

main.go
end_point
    end_point.go
    handlers.go
    handlers_test.go

main.go

package main

import (
	"chapter_09/end_point"
	"log"
)

func main() {
	log.Println("Let`s Starting ... ")
	end_point.EndPoint()
}

end_point.go

// Package end_point包提供用于网络服务的服务端口
package end_point

import (
	"fmt"
	"log"
	"net/http"
)

// EndPoint 服务入口
func EndPoint() {
	fmt.Println("Start our EndPoint ...")
	Routes()
	log.Println("Listener: Started :Listening on: 4000")
	http.ListenAndServe("4000", nil)
}

handlers.go

package end_point

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

// Routes 路由
func Routes() {
	http.HandleFunc("/sendjson", SendJSON)
}

// SendJSON 返回json数据
func SendJSON(rw http.ResponseWriter, r *http.Request) {
	u := struct {
		Name  string
		Email string
	}{
		Name:  "Rainmist",
		Email: "*********@qq.com",
	}

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

handlers_test.go

package end_point_test

import (
	"chapter_09/end_point"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
)

// 声明对号(√)
const checkMark = "\u2713"

// 声明对号(x)
const ballotX = "\u2717"

func init() {
	end_point.Routes()
}

// TestSendJSON 测试、sendjson内部服务端点
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("\tShould be create a request.", ballotX, err)
		}

		t.Log("\tShould be able to create a request.", checkMark)

		rw := httptest.NewRecorder()
		http.DefaultServeMux.ServeHTTP(rw, req)

		if rw.Code != 200 {
			t.Fatal("\tShould 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\tShould decode the response.", ballotX)
		}

		if u.Name == "Sinkmist" {
			t.Log("\tShould have a Email.", checkMark)
		} else {
			t.Error("\tShould have a Email.", ballotX, u.Email)
		}
	}
}

参考:Go语言实战

猜你喜欢

转载自blog.csdn.net/qq_40601372/article/details/100904029