0.1、索引

https://blog.waterflow.link/articles/1663688140724

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 纳秒。

golang单元测试一(简单函数测试)的更多相关文章

  1. Golang单元测试框架整理

    目录 一.单元测试是什么 二.单元测试的意义 三.Golang单元测试框架 3.1 Golang内置testing包 3.1.1 简单的测试 3.1.2 Benchmark 基准测试 3.1.3 运行 ...

  2. TODO:Golang UDP连接简单测试慎用Deadline

    TODO:Golang UDP连接简单测试慎用Deadline UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是OSI(Open System Interco ...

  3. Android单元测试与模拟测试详解

    测试与基本规范 为什么需要测试? 为了稳定性,能够明确的了解是否正确的完成开发. 更加易于维护,能够在修改代码后保证功能不被破坏. 集成一些工具,规范开发规范,使得代码更加稳定( 如通过 phabri ...

  4. 在Android Studio中进行单元测试和UI测试

    本篇教程翻译自Google I/O 2015中关于测试的codelab,掌握科学上网的同学请点击这里阅读:Unit and UI Testing in Android Studio.能力有限,如有翻译 ...

  5. Visual Studio 单元测试之五---数据库测试

    原文:Visual Studio 单元测试之五---数据库测试 数据库的单元测试主要是测试数据库中的数据是否符合特定的条件,Visual Studio 2010支持下面几种数据的单元测试类型(Visu ...

  6. Visual Studio 单元测试之三---压力测试

    原文:Visual Studio 单元测试之三---压力测试 我们都知道大名鼎鼎的LoadRuner,但是很少有人知道Visual Studio自带的Test也可以做些简单的压力测试,下面我们就介绍一 ...

  7. Visual Studio 单元测试之四---Generic测试

    原文:Visual Studio 单元测试之四---Generic测试 这里的Generic我觉得理解为外部测试更合适.因为在这种测试模式下Visual Studio只是启动一个外部的程序,然后通过返 ...

  8. Webpack单元测试,e2e测试

    此篇文章是续 webpack多入口文件.热更新等体验,主要说明单元测试与e2e测试的基本配置以及相关应用. 一.单元测试 实现单元测试框架的搭建.es6语法的应用.以及测试覆盖率的引入. 1. 需要安 ...

  9. ARTS-S golang单元测试

    golang单元测试 在$GOPATH的src目录下建目录demo_unittest 在目录demo_unittest下建文件calc.go,内容如下: package demo_unittest f ...

  10. 用量子计算模拟器ProjectQ生成随机数,并用pytest进行单元测试与覆盖率测试

    技术背景 本文中主要包含有三个领域的知识点:随机数的应用.量子计算模拟产生随机数与基于pytest框架的单元测试与覆盖率测试,这里先简单分别介绍一下背景知识. 随机数的应用 在上一篇介绍量子态模拟采样 ...

随机推荐

  1. feign远程调用出错

    如果你传递的参数,比较复杂时,默认会采用POST的请求方式. 传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,Feign接口中不 ...

  2. 升级CentOS 7 内核版本

    1.查看当前内核版本 $uname -r 3.10.0-957.el7.x86_64 $uname -a Linux prometheus 3.10.0-957.el7.x86_64 #1 SMP T ...

  3. [CF1537E] Erase and Extend (字符串)

    题面 给一个长度为 n \tt n n 的字符串,你可以进行无限次以下两种操作之一: 删去末尾的字符(此时要保证删去后字符串非空). 把当前整个字符串复制一份,接到自己的后面. 输出最终通过操作能达到 ...

  4. 【LOJ#3197】【eJOI2019】T形覆盖 - (图论、简单推导)

    题面 题解 (题目中说的四种摆放方式实际上是分别旋转0°,90°,180°,270°后的图形) 题目中关于摆放方式的描述听起来很臭,我们把它转换一下,每个拼版先覆盖"上下左右中"五 ...

  5. Centroids (换根DP)

    题面 题解 删一条边.加一条边,相当于把一个子树折下来,然后嫁接在一个点上, 那么最优的情况肯定是接在根上,对吧,很好理解吧 那么这个拆下来的子树大小就不能超过n/2. 我们用son[]来表示每个点为 ...

  6. java数组---概念

    1.数组的定义 数组是相同类型数据的有序集合.数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们. 2.数组的建 ...

  7. JSP中的EL 表达式

    JSP中的EL 表达式 什么是 EL 表达式,EL 表达式的作用? EL 表达式的全称是:Expression Language.是表达式语言. EL 表达式的什么作用:EL 表达式主要是代替 jsp ...

  8. Dynamic CRM插件中记录日志-Nlog记录到文本

    Dynamic CRM插件中记录日志的方式有多种 通常情况下分为ITracingService记录.单独日志表插入记录.文本记录三种. 之前整理过ITracingService记录的方式,但这种记录有 ...

  9. 08_Linux基础-vim-tmux-字符编码

    @ 目录 08_Linux基础-vim-tmux-字符编码 一. vim vim编辑器作用 vim模式 vim命令模式 vim编辑模式 vim末行模式 vim视图模式 vim替换模式 练习 vim常用 ...

  10. 使用Kali的wifite和aircrack-ng联合破解wifi密码

    准备材料 有kali的虚拟机,这里推荐VM 一个超级便宜的USB无线网卡,很便宜三十几块钱 一个靠谱的WPA密码字典(关于字典文件,我这里整理了好多,可联系我.QQ:1213456261) 1.运行k ...