0.1、索引
blog.waterflow.link/articles/16636...
1、简介
单元测试是测试代码、组件和模块的单元函数。单元测试的目的是清除代码中的错误,增加代码的稳定性,在更改代码时提供正确性。单元测试是软件测试的第一级,然后是集成测试和 ui 测试。
2、编写测试代码
首先测试文件的命名必须以_test.go
结尾,测试方法必须以Test
开头
我们创建一个testexample项目,执行go mod init
初始化项目。
然后创建一个uri.go的文件,里面的代码是我摘抄自golang的amqp包中的一段解析ampq url的代码,具体链接
package uri | |
import ( | |
"errors" | |
"net" | |
"net/url" | |
"strconv" | |
"strings" | |
) | |
var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'") | |
var errURIWhitespace = errors.New("URI must not contain whitespace") | |
var schemePorts = map[string]int{ | |
"amqp": 5672, | |
"amqps": 5671, | |
} | |
var defaultURI = URI{ | |
Scheme: "amqp", | |
Host: "localhost", | |
Port: 5672, | |
Username: "guest", | |
Password: "guest", | |
Vhost: "/", | |
} | |
// URI represents a parsed AMQP URI string. | |
type URI struct { | |
Scheme string | |
Host string | |
Port int | |
Username string | |
Password string | |
Vhost string | |
} | |
// ParseURI attempts to parse the given AMQP URI according to the spec. | |
// See http://www.rabbitmq.com/uri-spec.html. | |
// | |
// Default values for the fields are: | |
// | |
// Scheme: amqp | |
// Host: localhost | |
// Port: 5672 | |
// Username: guest | |
// Password: guest | |
// Vhost: / | |
// | |
func ParseURI(uri string) (URI, error) { | |
builder := defaultURI | |
// 如果链接中有空字符串,返回默认值和错误 | |
if strings.Contains(uri, " ") { | |
return builder, errURIWhitespace | |
} | |
// 解析url为结构体,解析失败返回默认值和错误 | |
u, err := url.Parse(uri) | |
if err != nil { | |
return builder, err | |
} | |
// 根据scheme获取默认端口 | |
defaultPort, okScheme := schemePorts[u.Scheme] | |
if okScheme { | |
builder.Scheme = u.Scheme | |
} else { | |
// 获取不到就返回默认值和错误 | |
return builder, errURIScheme | |
} | |
host := u.Hostname() | |
port := u.Port() | |
if host != "" { | |
builder.Host = host | |
} | |
if port != "" { | |
port32, err := strconv.ParseInt(port, 10, 32) | |
if err != nil { | |
// 解析出来的端口转整型失败,返回最新的URI和错误 | |
return builder, err | |
} | |
builder.Port = int(port32) | |
} else { | |
builder.Port = defaultPort | |
} | |
if u.User != nil { | |
builder.Username = u.User.Username() | |
if password, ok := u.User.Password(); ok { | |
builder.Password = password | |
} | |
} | |
if u.Path != "" { | |
if strings.HasPrefix(u.Path, "/") { | |
if u.Host == "" && strings.HasPrefix(u.Path, "///") { | |
// net/url doesn't handle local context authorities and leaves that up | |
// to the scheme handler. In our case, we translate amqp:/// into the | |
// default host and whatever the vhost should be | |
if len(u.Path) > 3 { | |
builder.Vhost = u.Path[3:] | |
} | |
} else if len(u.Path) > 1 { | |
builder.Vhost = u.Path[1:] | |
} | |
} else { | |
builder.Vhost = u.Path | |
} | |
} | |
return builder, nil | |
} | |
func (uri URI) String() string { | |
authority, err := url.Parse("") | |
if err != nil { | |
return err.Error() | |
} | |
authority.Scheme = uri.Scheme | |
if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password { | |
authority.User = url.User(uri.Username) | |
if uri.Password != defaultURI.Password { | |
authority.User = url.UserPassword(uri.Username, uri.Password) | |
} | |
} | |
authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) | |
if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port { | |
authority.Host = net.JoinHostPort(uri.Host, strconv.Itoa(uri.Port)) | |
} else { | |
// JoinHostPort() automatically add brackets to the host if it's | |
// an IPv6 address. | |
// | |
// If not port is specified, JoinHostPort() return an IP address in the | |
// form of "[::1]:", so we use TrimSuffix() to remove the extra ":". | |
authority.Host = strings.TrimSuffix(net.JoinHostPort(uri.Host, ""), ":") | |
} | |
if uri.Vhost != defaultURI.Vhost { | |
// Make sure net/url does not double escape, e.g. | |
// "%2F" does not become "%252F". | |
authority.Path = uri.Vhost | |
authority.RawPath = url.QueryEscape(uri.Vhost) | |
} else { | |
authority.Path = "/" | |
} | |
return authority.String() | |
} |
先不用考虑上面函数的复杂性,我们的目的很简单,就是要测试ParseURI
函数。那如何测试呢?首先我们需要创建一个uri_test.go
文件,一般和需要测试的uri.go
在同一个目录下。其次文件的包名也需要和uri.go
一致。
然后我们就可以编写测试函数了,上面说了测试函数需要以Test
开头。
我们先写一个简单的测试函数:
package uri | |
import ( | |
"testing" | |
) | |
// Test matrix defined on http://www.rabbitmq.com/uri-spec.html | |
type testURI struct { | |
url string | |
username string | |
password string | |
host string | |
port int | |
vhost string | |
canon string | |
} | |
var uriTest = testURI{ | |
url: "amqp://user:pass@host:10000/vhost", | |
username: "user", | |
password: "pass", | |
host: "host", | |
port: 10000, | |
vhost: "vhost", | |
canon: "amqp://user:pass@host:10000/vhost", | |
} | |
func TestUri(t *testing.T) { | |
u, err := ParseURI(uriTest.url) | |
if err != nil { | |
t.Fatal("Could not parse spec URI: ", uriTest.url, " err: ", err) | |
} | |
if uriTest.username != u.Username { | |
t.Error("For: ", uriTest.url, " usernames do not match. want: ", uriTest.username, " got: ", u.Username) | |
} else { | |
t.Log("For: ", uriTest.url, " usernames match. want: ", uriTest.username, " got: ", u.Username) | |
} | |
} |
首先我们看到包名是uri
,然后我们定义了一个以Test
开头的测试函数TestUri
。函数的入参就代表这是一个单元测试。
然后我们定义一个需要测试的url的结构testURI
,然后初始化结构体uriTest
。其中uriTest.url
就是需要传入ParseURI
的参数,另外的一些参数就是解析之后希望返回的结果。
可以看到上面有几个方法:
- t.Fatal,测试错误并退出
- t.Error,测试错误会继续往下执行
- t.Log,一般代表测试成功打印日志
3、运行测试代码
然后我们在命令行执行下这个测试函数:
go test -run TestUri | |
PASS | |
ok go-demo/testexample/uri 0.390s |
从执行结果我们可以看到测试成功了,但是并没有打印成功的日志。这是因为我们需要加上一个参数,修改如下:
go test -v -run TestUri | |
=== RUN TestUri | |
uri_test.go:37: For: amqp://user:pass@host:10000/vhost usernames match. want: user got: user | |
--- PASS: TestUri (0.00s) | |
PASS | |
ok go-demo/testexample/uri 0.120s |
可以看到测试成功的日志打印出来了。
-run
代表可以指定某个具体的测试函数执行,如果去掉的话也可以,会执行这个目录下所有的测试函数
4、表驱动测试
如果我们想执行一批测试代码,这个时候应该怎么写呢?很简单,我们只需要定义一个切片就行:
package uri | |
import ( | |
"testing" | |
) | |
// Test matrix defined on http://www.rabbitmq.com/uri-spec.html | |
type testURI struct { | |
url string | |
username string | |
password string | |
host string | |
port int | |
vhost string | |
canon string | |
} | |
var uriTests = []testURI{ | |
{ | |
url: "amqp://user:pass@host:10000/vhost", | |
username: "user", | |
password: "pass", | |
host: "host", | |
port: 10000, | |
vhost: "vhost", | |
canon: "amqp://user:pass@host:10000/vhost", | |
}, | |
{ | |
url: "amqp://", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://localhost/", | |
}, | |
{ | |
url: "amqp://:@/", | |
username: "", | |
password: "", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://:@localhost/", | |
}, | |
{ | |
url: "amqp://user@", | |
username: "user", | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://user@localhost/", | |
}, | |
{ | |
url: "amqp://user:pass@", | |
username: "user", | |
password: "pass", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://user:pass@localhost/", | |
}, | |
{ | |
url: "amqp://guest:pass@", | |
username: "guest", | |
password: "pass", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://guest:pass@localhost/", | |
}, | |
{ | |
url: "amqp://host", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://:10000", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: 10000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://localhost:10000/", | |
}, | |
{ | |
url: "amqp:///vhost", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: "vhost", | |
canon: "amqp://localhost/vhost", | |
}, | |
{ | |
url: "amqp://host/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://host/%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "/", | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://host/%2F%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "//", | |
canon: "amqp://host/%2F%2F", | |
}, | |
{ | |
url: "amqp://host/%2Fslash%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "/slash/", | |
canon: "amqp://host/%2Fslash%2F", | |
}, | |
{ | |
url: "amqp://192.168.1.1:1000/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "192.168.1.1", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://192.168.1.1:1000/", | |
}, | |
{ | |
url: "amqp://[::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[::1]/", | |
}, | |
{ | |
url: "amqp://[::1]:1000", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "::1", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[::1]:1000/", | |
}, | |
{ | |
url: "amqp://[fe80::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]/", | |
}, | |
{ | |
url: "amqp://[fe80::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]/", | |
}, | |
{ | |
url: "amqp://[fe80::1%25en0]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1%en0", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1%25en0]/", | |
}, | |
{ | |
url: "amqp://[fe80::1]:5671", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: 5671, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]:5671/", | |
}, | |
{ | |
url: "amqps:///", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: schemePorts["amqps"], | |
vhost: defaultURI.Vhost, | |
canon: "amqps://localhost/", | |
}, | |
{ | |
url: "amqps://host:1000/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqps://host:1000/", | |
}, | |
} | |
func TestURISpec(t *testing.T) { | |
for _, test := range uriTests { | |
u, err := ParseURI(test.url) | |
if err != nil { | |
t.Fatal("Could not parse spec URI: ", test.url, " err: ", err) | |
} | |
if test.username != u.Username { | |
t.Error("For: ", test.url, " usernames do not match. want: ", test.username, " got: ", u.Username) | |
} | |
if test.password != u.Password { | |
t.Error("For: ", test.url, " passwords do not match. want: ", test.password, " got: ", u.Password) | |
} | |
if test.host != u.Host { | |
t.Error("For: ", test.url, " hosts do not match. want: ", test.host, " got: ", u.Host) | |
} | |
if test.port != u.Port { | |
t.Error("For: ", test.url, " ports do not match. want: ", test.port, " got: ", u.Port) | |
} | |
if test.vhost != u.Vhost { | |
t.Error("For: ", test.url, " vhosts do not match. want: ", test.vhost, " got: ", u.Vhost) | |
} | |
if test.canon != u.String() { | |
t.Error("For: ", test.url, " canonical string does not match. want: ", test.canon, " got: ", u.String()) | |
} | |
} | |
} | |
uriTests
是我们定义的一个切片,只需要在测试函数中遍历以下即可。
5、测试覆盖率
测试覆盖率是应用程序中代码覆盖率的测量百分比。了解测试覆盖了多少代码很重要。通过这种方式,您可以看到代码的哪些部分您已经测试过,哪些部分我们没有测试过。
Go 的标准库提供了内置的测试覆盖来检查你的代码覆盖率。
我们修改下测试代码如下:
package uri | |
import ( | |
"testing" | |
) | |
// Test matrix defined on http://www.rabbitmq.com/uri-spec.html | |
type testURI struct { | |
url string | |
username string | |
password string | |
host string | |
port int | |
vhost string | |
canon string | |
} | |
var uriTests = []testURI{ | |
{ | |
url: "amqp://user:pass@host:10000/vhost", | |
username: "user", | |
password: "pass", | |
host: "host", | |
port: 10000, | |
vhost: "vhost", | |
canon: "amqp://user:pass@host:10000/vhost", | |
}, | |
{ | |
url: "amqp://", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://localhost/", | |
}, | |
{ | |
url: "amqp://:@/", | |
username: "", | |
password: "", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://:@localhost/", | |
}, | |
{ | |
url: "amqp://user@", | |
username: "user", | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://user@localhost/", | |
}, | |
{ | |
url: "amqp://user:pass@", | |
username: "user", | |
password: "pass", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://user:pass@localhost/", | |
}, | |
{ | |
url: "amqp://guest:pass@", | |
username: "guest", | |
password: "pass", | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://guest:pass@localhost/", | |
}, | |
{ | |
url: "amqp://host", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://:10000", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: 10000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://localhost:10000/", | |
}, | |
{ | |
url: "amqp:///vhost", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: defaultURI.Port, | |
vhost: "vhost", | |
canon: "amqp://localhost/vhost", | |
}, | |
{ | |
url: "amqp://host/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://host/%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "/", | |
canon: "amqp://host/", | |
}, | |
{ | |
url: "amqp://host/%2F%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "//", | |
canon: "amqp://host/%2F%2F", | |
}, | |
{ | |
url: "amqp://host/%2Fslash%2F", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: defaultURI.Port, | |
vhost: "/slash/", | |
canon: "amqp://host/%2Fslash%2F", | |
}, | |
{ | |
url: "amqp://192.168.1.1:1000/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "192.168.1.1", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://192.168.1.1:1000/", | |
}, | |
{ | |
url: "amqp://[::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[::1]/", | |
}, | |
{ | |
url: "amqp://[::1]:1000", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "::1", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[::1]:1000/", | |
}, | |
{ | |
url: "amqp://[fe80::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]/", | |
}, | |
{ | |
url: "amqp://[fe80::1]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]/", | |
}, | |
{ | |
url: "amqp://[fe80::1%25en0]", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1%en0", | |
port: defaultURI.Port, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1%25en0]/", | |
}, | |
{ | |
url: "amqp://[fe80::1]:5671", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "fe80::1", | |
port: 5671, | |
vhost: defaultURI.Vhost, | |
canon: "amqp://[fe80::1]:5671/", | |
}, | |
{ | |
url: "amqps:///", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: defaultURI.Host, | |
port: schemePorts["amqps"], | |
vhost: defaultURI.Vhost, | |
canon: "amqps://localhost/", | |
}, | |
{ | |
url: "amqps://host:1000/", | |
username: defaultURI.Username, | |
password: defaultURI.Password, | |
host: "host", | |
port: 1000, | |
vhost: defaultURI.Vhost, | |
canon: "amqps://host:1000/", | |
}, | |
} | |
func TestURISpec(t *testing.T) { | |
for _, test := range uriTests { | |
u, err := ParseURI(test.url) | |
if err != nil { | |
t.Fatal("Could not parse spec URI: ", test.url, " err: ", err) | |
} | |
if test.username != u.Username { | |
t.Error("For: ", test.url, " usernames do not match. want: ", test.username, " got: ", u.Username) | |
} | |
if test.password != u.Password { | |
t.Error("For: ", test.url, " passwords do not match. want: ", test.password, " got: ", u.Password) | |
} | |
if test.host != u.Host { | |
t.Error("For: ", test.url, " hosts do not match. want: ", test.host, " got: ", u.Host) | |
} | |
if test.port != u.Port { | |
t.Error("For: ", test.url, " ports do not match. want: ", test.port, " got: ", u.Port) | |
} | |
if test.vhost != u.Vhost { | |
t.Error("For: ", test.url, " vhosts do not match. want: ", test.vhost, " got: ", u.Vhost) | |
} | |
if test.canon != u.String() { | |
t.Error("For: ", test.url, " canonical string does not match. want: ", test.canon, " got: ", u.String()) | |
} | |
} | |
} | |
func TestURIUnknownScheme(t *testing.T) { | |
if _, err := ParseURI("http://example.com/"); err == nil { | |
t.Fatal("Expected error when parsing non-amqp scheme") | |
} | |
} | |
func TestURIScheme(t *testing.T) { | |
if _, err := ParseURI("amqp://example.com/"); err != nil { | |
t.Fatalf("Expected to parse amqp scheme, got %v", err) | |
} | |
if _, err := ParseURI("amqps://example.com/"); err != nil { | |
t.Fatalf("Expected to parse amqps scheme, got %v", err) | |
} | |
} | |
func TestURIWhitespace(t *testing.T) { | |
if _, err := ParseURI("amqp://admin:PASSWORD@rabbitmq-service/ -http_port=8080"); err == nil { | |
t.Fatal("Expected to fail if URI contains whitespace") | |
} | |
} | |
func TestURIDefaults(t *testing.T) { | |
url := "amqp://" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.String() != "amqp://localhost/" { | |
t.Fatal("Defaults not encoded properly got:", uri.String()) | |
} | |
} | |
func TestURIComplete(t *testing.T) { | |
url := "amqp://bob:dobbs@foo.bar:5678/private" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.String() != url { | |
t.Fatal("Defaults not encoded properly want:", url, " got:", uri.String()) | |
} | |
} | |
func TestURIDefaultPortAmqpNotIncluded(t *testing.T) { | |
url := "amqp://foo.bar:5672/" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.String() != "amqp://foo.bar/" { | |
t.Fatal("Defaults not encoded properly got:", uri.String()) | |
} | |
} | |
func TestURIDefaultPortAmqp(t *testing.T) { | |
url := "amqp://foo.bar/" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.Port != 5672 { | |
t.Fatal("Default port not correct for amqp, got:", uri.Port) | |
} | |
} | |
func TestURIDefaultPortAmqpsNotIncludedInString(t *testing.T) { | |
url := "amqps://foo.bar:5671/" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.String() != "amqps://foo.bar/" { | |
t.Fatal("Defaults not encoded properly got:", uri.String()) | |
} else { | |
t.Logf("ParseURI(%s) execute success\n", url) | |
} | |
} | |
func TestURIDefaultPortAmqps(t *testing.T) { | |
url := "amqps://foo.bar/" | |
uri, err := ParseURI(url) | |
if err != nil { | |
t.Fatal("Could not parse") | |
} | |
if uri.Port != 5671 { | |
t.Fatal("Default port not correct for amqps, got:", uri.Port) | |
} | |
} | |
然后执行:
go test -cover | |
PASS | |
coverage: 88.0% of statements | |
ok go-demo/testexample/uri 0.385s |
可以看到测试通过,覆盖率是88%。说明我们还有一些代码分支没有测试到,那我们如何去看这些没有覆盖到的代码分支呢?
很简单,还是go test
命令:
go test -coverprofile=cover_out | |
PASS | |
coverage: 88.0% of statements | |
ok go-demo/testexample/uri 0.119s |
执行完成后我们可以看到目录中多了cover_out
文件
tree | |
. | |
├── cover_out | |
├── uri.go | |
└── uri_test.go |
我们看下这个文件里能找到没有覆盖到的代码么?
mode: set | |
go-demo/testexample/uri/uri.go:50.40,54.32 2 1 | |
go-demo/testexample/uri/uri.go:59.2,60.16 2 1 | |
go-demo/testexample/uri/uri.go:65.2,67.14 2 1 | |
go-demo/testexample/uri/uri.go:74.2,77.16 3 1 | |
go-demo/testexample/uri/uri.go:81.2,81.16 1 1 | |
go-demo/testexample/uri/uri.go:92.2,92.19 1 1 | |
go-demo/testexample/uri/uri.go:99.2,99.18 1 1 | |
go-demo/testexample/uri/uri.go:116.2,116.21 1 1 | |
go-demo/testexample/uri/uri.go:54.32,56.3 1 1 | |
go-demo/testexample/uri/uri.go:60.16,62.3 1 0 | |
go-demo/testexample/uri/uri.go:67.14,69.3 1 1 | |
go-demo/testexample/uri/uri.go:69.8,72.3 1 1 | |
go-demo/testexample/uri/uri.go:77.16,79.3 1 1 | |
go-demo/testexample/uri/uri.go:81.16,83.17 2 1 | |
go-demo/testexample/uri/uri.go:87.3,87.29 1 1 | |
go-demo/testexample/uri/uri.go:83.17,86.4 1 0 | |
go-demo/testexample/uri/uri.go:88.8,90.3 1 1 | |
go-demo/testexample/uri/uri.go:92.19,94.44 2 1 | |
go-demo/testexample/uri/uri.go:94.44,96.4 1 1 | |
go-demo/testexample/uri/uri.go:99.18,100.37 1 1 | |
go-demo/testexample/uri/uri.go:100.37,101.56 1 1 | |
go-demo/testexample/uri/uri.go:101.56,105.24 1 0 | |
go-demo/testexample/uri/uri.go:105.24,107.6 1 0 | |
go-demo/testexample/uri/uri.go:108.10,108.30 1 1 | |
go-demo/testexample/uri/uri.go:108.30,110.5 1 1 | |
go-demo/testexample/uri/uri.go:111.9,113.4 1 0 | |
go-demo/testexample/uri/uri.go:119.32,121.16 2 1 | |
go-demo/testexample/uri/uri.go:125.2,127.80 2 1 | |
go-demo/testexample/uri/uri.go:135.2,137.86 2 1 | |
go-demo/testexample/uri/uri.go:148.2,148.35 1 1 | |
go-demo/testexample/uri/uri.go:157.2,157.27 1 1 | |
go-demo/testexample/uri/uri.go:121.16,123.3 1 0 | |
go-demo/testexample/uri/uri.go:127.80,130.42 2 1 | |
go-demo/testexample/uri/uri.go:130.42,132.4 1 1 | |
go-demo/testexample/uri/uri.go:137.86,139.3 1 1 | |
go-demo/testexample/uri/uri.go:139.8,146.3 1 1 | |
go-demo/testexample/uri/uri.go:148.35,153.3 2 1 | |
go-demo/testexample/uri/uri.go:153.8,155.3 1 1 |
看的我是一脸懵逼,不过别着急我们可以把这个文件转成我们想要的html格式:
go tool cover -html=cover_out -o cover_out.html
看下目录中是不是多了个html
tree | |
. | |
├── cover_out | |
├── cover_out.html | |
├── uri.go | |
└── uri_test.go |
我们浏览器打开这个文件,会看到下面这个界面,不要太爽:
红色的就是代表我们测试没有覆盖到的。只需要丰富下测试代码,就可以做到100%覆盖了。这部分就由你自己完成吧。
6、基准测试
通过 Benchmarking,您可以衡量代码的性能并查看您对代码所做的更改的影响,从而优化您的源代码。
文件名必须以 Benchmark 前缀作为单元测试文件名约定。(BenchmarkSomething(*testing.B))
package uri | |
import ( | |
"testing" | |
) | |
// Test matrix defined on http://www.rabbitmq.com/uri-spec.html | |
type testURI struct { | |
url string | |
username string | |
password string | |
host string | |
port int | |
vhost string | |
canon string | |
} | |
var uriTest = testURI{ | |
url: "amqp://user:pass@host:10000/vhost", | |
username: "user", | |
password: "pass", | |
host: "host", | |
port: 10000, | |
vhost: "vhost", | |
canon: "amqp://user:pass@host:10000/vhost", | |
} | |
func BenchmarkUri(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
_, _ = ParseURI(uriTest.url) | |
} | |
} |
- 基准函数必须带参数测试。B
- Benchmark 必须运行 N 次testing.B提供(N 是整数类型,从 Go 调整)
- 当提供 -bench 标志时,由“go test”命令执行。
- 该
-bench
标志以正则表达式的形式接受其参数。
执行命令如下:
go test -bench=. | |
goos: darwin | |
goarch: amd64 | |
pkg: go-demo/testexample/uri | |
cpu: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz | |
BenchmarkUri-8 2041880 579.2 ns/op | |
PASS | |
ok go-demo/testexample/uri 2.075s |
基准测试结果意味着基准测试通过。循环运行 2041880 次,每个循环速度为 579.2 纳秒。