可以使用: go test -v 在文件所在位置测试代码
go test -v
-v 提供 冗余输出: 如果不加 “-v” 除非测试失败,否则无法看到任何测试输出
Note:
- go 语言的测试工具只会认为以 _test.go 结尾的文件是测试文件,一旦测试工具找到了测试文件,就会找到里面的测试函数并执行。
注:如果没有遵循该约定,就不会有测试报告
- 一个测试函数必须是公开的函数,以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语言实战