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 uriimport ("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   stringHost     stringPort     intUsername stringPassword stringVhost    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 beif 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.Schemeif 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.Vhostauthority.RawPath = url.QueryEscape(uri.Vhost)} else {authority.Path = "/"}return authority.String()
}

先不用考虑上面函数的复杂性,我们的目的很简单,就是要测试ParseURI函数。那如何测试呢?首先我们需要创建一个uri_test.go文件,一般和需要测试的uri.go在同一个目录下。其次文件的包名也需要和uri.go 一致。

然后我们就可以编写测试函数了,上面说了测试函数需要以Test开头。

我们先写一个简单的测试函数:

package uriimport ("testing"
)// Test matrix defined on http://www.rabbitmq.com/uri-spec.html
type testURI struct {url      stringusername stringpassword stringhost     stringport     intvhost    stringcanon    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   TestUriuri_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 uriimport ("testing"
)// Test matrix defined on http://www.rabbitmq.com/uri-spec.html
type testURI struct {url      stringusername stringpassword stringhost     stringport     intvhost    stringcanon    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 uriimport ("testing"
)// Test matrix defined on http://www.rabbitmq.com/uri-spec.html
type testURI struct {url      stringusername stringpassword stringhost     stringport     intvhost    stringcanon    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 uriimport ("testing"
)// Test matrix defined on http://www.rabbitmq.com/uri-spec.html
type testURI struct {url      stringusername stringpassword stringhost     stringport     intvhost    stringcanon    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单元测试、Mock测试以及基准测试

    之前参加字节跳动青训营而整理的笔记 Golang拥有一套单元测试和性能测试系统,仅需要添加很少的代码就可以快速测试一段需求代码. 一.单元测试 单元测试主要包括:输入.测试单元.输出.期望以及与期望的 ...

  2. ​手把手教你如何进行 Golang 单元测试

    作者:stevennzhou,腾讯 PCG 前端开发工程师 本篇是对单元测试的一个总结,通过完整的单元测试手把手教学,能够让刚接触单元测试的开发者从整体上了解一个单元测试编写的全过程.最终通过两个问题 ...

  3. golang 单元测试和性能测试

    单元测试 单元测试(Unit Tests, UT) 是一个优秀项目不可或缺的一部分,特别是在一些频繁变动和多人合作开发的项目中尤为重要.你或多或少都会有因为自己的提交,导致应用挂掉或服务宕机的经历.比 ...

  4. Golang单元测试坑盘点

    Golang单元测试坑盘点 最近在公司写单元测试,发现了不少坑.例如:monkey不支持inline函数,vscode单测有缓存,convey对切片.map等比较不可以直接使用类似于==进行.本节呢, ...

  5. Golang单元测试与覆盖率

    1 概述 C/C++和Java(以及大多数的主流编程语言)都有自己成熟的单元测试框架,前者如Check,后者如JUnit,但这些编程框架本质上仍是第三方产品,为了执行单元测试,我们不得不从头开始搭建测 ...

  6. VSCode配置Golang单元测试实例

    目录 前言 正文 一.导入testing工具包 二.单元测试文件命名规范 三.单元测试方法命名规范 四.执行单元测试 结尾 前言 说到代码的健壮性,单元测试是少不了的,基本上所有语言都有自己的单元测试 ...

  7. Webpack单元测试,e2e测试

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

  8. 主管问我:你以为单元测试,只是测试吗?

    问:单元测试,只是测试吗? 答:单元测试除了是一种测试手段外,更是一种改善代码设计的工具,容易写单测的代码往往也具有更加良好的设计. 因而是任何自动化测试工具都无法取代的. 当然,这里也不是把自动化测 ...

  9. GoLang之go test测试

    文章目录 GoLang之go test测试 1.介绍 2.函数 3.测试函数格式 3.1格式 3.2失败示例 3.3成功示例1 3.4成功示例2 3.5成功示例3 3.6成功实例4 4.基准函数 4. ...

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

    目录 在Android Studio中进行单元测试和UI测试 - 1.概述 在Android Studio中进行单元测试和UI测试 - 2.创建新的Android Studio工程 在Android ...

最新文章

  1. 网页上加在线客服代码QQ,MSN,skype,goolge TALK,雅虎通,贸易通,淘宝旺旺
  2. java_spring_依赖注入(构造器)
  3. android视图工具,android studio的HierarchyViewer工具如何知道android屏幕的视图属性
  4. 黑苹果找不到触控板_猫和老鼠手游:好友位不足却找不到人开黑?玩家给好友系统支招...
  5. 人工智能安全框架(2020年)
  6. Collection集合 创建/添加/迭代
  7. NOIP模拟测试25
  8. 论文笔记_S2D.36_2017-CVPR_CNN-SLAM: 实时稠密单目SLAM与学习深度预测
  9. Java 的简单内嵌浏览器--最新版jxbrowser 6.21破解
  10. RAID - 提升IO性能及数据安全
  11. Rfb-Net(代码解读慢慢啃).
  12. 今日恐慌与贪婪指数为22 恐慌程度有所缓解
  13. 索尼和微软同时发布新游戏机——抢钱了
  14. 安卓期末大作业——番茄小说APP
  15. ubuntu的换源问题
  16. 12.unity编程基础
  17. 枚举的应用:熄灯问题讨厌的青蛙
  18. 泛微OA发送邮件【E8、E9】
  19. 2019如何成为一个优秀的程序员
  20. nonebot2聊天机器人插件9:定时提醒器timing

热门文章

  1. hfss和python_用python自动化仿真HFSS,超简易
  2. docker load -i 导入后 看不到镜像
  3. 马斯克“大脑改造计划”,还需要突破哪几道技术门槛?
  4. 夏盈盈:4.17主流货币凌晨重回小牛市,看涨追涨看跌杀跌为何套单的总是你?...
  5. 未在本地计算机上注册“Microsoft.Jet.OLEDB.4.0”/Microsoft.ACE.OLEDB.12.0”提供程序
  6. 怎么在图片上直接编辑文字?分享两个编辑图片的方法
  7. Java上传文件内容
  8. 从零开始的学JAVA生活day_02
  9. Flink之轻量级分布式快照Checkpoint
  10. 电商用户购买行为预测-排名48-0.23