标签 单元测试 下的文章

聊聊godoc、go doc与pkgsite

本文永久链接 – https://tonybai.com/2023/03/20/godoc-vs-go-doc-vs-pkgsite

就像上一篇文章聊到的Go内置单元测试框架一样,既重视语言特性,又不忘对Go软件项目提供整体环境特性的Go在诞生伊始就定义了如何在源码中通过注释编写代码文档的格式,并提供了基于代码注释实时生成Go文档并支持文档查看的工具。

而一些早期的语言,比如C、C++等则需要使用第三方工具(如doxygen)以及这些工具规定的特定格式编写文档,缺少语言原生的文档标准与工具,给后期开发人员之间的协作带去了麻烦。

查看文档是开发人员日常必不可少的开发活动之一。Go语言从诞生那天起就十分重视项目文档的建设,为此Go为gopher们提供了多种丰富的文档查看工具,除了在Go官方网站可以在线查看到最新稳定发布版的文档之外,Go还为开发人员提供了本地离线查看文档的工具,比如:godoc、go doc以及pkgsite。在这篇短文中,我们就来分别看看这三个Go文档查看工具。

一. godoc

很多接触Go语言较早的gopher都知道,Go安装包中曾原生自带了一个和go、gofmt一起发布的文档查看工具:godoc。它也是Go的第一个文档查看工具

godoc实质上是一个web服务,它会在本地离线建立起一个web形式的Go文档中心,对本地安装的go包提供文档查看服务。

当我们执行下面命令时这个文档中心服务就启动了:

$godoc -http=localhost:8080

在浏览器地址栏输入http://localhost:8080打开Go文档中心首页,godoc默认会展示\$GOROOT下的目录结构:

我们看到首页顶部的菜单与Go旧版官方主页的菜单基本如出一辙。

再点击Packages我们会看到godoc会展示本地包的参考文档页面:

Go包参考文档页面将包分为几类:标准库包(Standard library)、第三方包(Third party)和其它包(Other packages),其中的第三方包就是本地\$GOPATH下面的各个包。

在“Packages”页面中的Standard Library下面找到标准库io包,点击打开Go io包的参考文档页面如下图所示:

这样我们就可以离线以web页面的形式查看go module相关文档了! Go 1.13版本之前,这就像是在本地建立一个Go官方站点的mirror site。

并且,godoc支持-play命令行选项,可以启动playground功能,go文档中的example也可以像online playground那样运行:

不过这个功能不是离线的,不能使用本机的Go编译器和环境运行,需要连接网络进行。

Godoc还支持查看历史版本的Go文档,这个之前写过,大家可以移步阅读。

接下来聊聊godoc这个工具的现状!很遗憾,从Go 1.13版本开始,godoc就失去了官方工具的地位,不再和go、gofmt一起内置在Go安装包中发布了!如果你想使用godoc,需要使用下面命令自行安装:

$go install golang.org/x/tools/cmd/godoc@latest

随着2019年Go新官方站点的发布,godoc风格的web文档查看方式渐渐被人遗忘了!godoc.org也关闭了。

2021年末,godoc工具也被标记为deprecated了(虽然这两年还有几个commit),标志着godoc正式退出历史舞台!

注:怀旧的gopher建立了godoc.org的替代站点:https://godocs.io,由Go社区维护。

那么,没有了godoc,我们如何离线查询go文档呢?我们接下来来聊聊本地查看go文档的命令行工具go doc。

二. go doc

go doc是Go语言自带的命令行工具,可以用来查看本地安装的Go包的文档。与godoc不同的是,go doc不需要启动HTTP服务器,直接在终端中使用即可:

自go doc在Go 1.5版本加入Go工具链之后,它就和go get、go build一样成为了Gopher们每日必用的go子命令。

在查看包文档时,go doc在命令行上接受的参数使用了Go语法的格式,这使得go doc的上手使用几乎是“零门槛”:

go doc <pkg>
go doc <sym>[.<methodOrField>]
go doc [<pkg>.]<sym>[.<methodOrField>]
go doc [<pkg>.][<sym>.]<methodOrField>

下面我们就来简要介绍一下如何使用go doc查看各类包文档。

  • 查看标准库文档

我们可以在任意路径下执行go doc命令查看标准库文档,下面是一些查看标准库不同元素文档的命令示例。

查看标准库net/http包文档:

$go doc net/http
或
$go doc http

查看http包的Get函数的文档:

$ go doc net/http.Get
或
$ go doc http.Get

查看http包中结构体类型Requset中字段Form的文档:

$go doc net/http.Request.Form
或
$go doc http.Request.Form
  • 查看当前项目文档

除了查看标准库文档,我们在从事项目开发时很可能会查看当前项目中其他包的文档以决定如何使用这些包。go doc也可以很方便地查看当前路径下项目的文档,我们还以已经下载到本地(比如:~/temp/gocmpp)的github.com/bigwhite/gocmpp项目为例。

查看当前路径下的包的文档:

$go doc 

package cmpp // import "github.com/bigwhite/gocmpp"

const CmppActiveTestReqPktLen uint32 = 12 ...
const CmppConnReqPktLen uint32 = 4 + 4 + 4 + 6 + 16 + 1 + 4 ...
const Cmpp2DeliverReqPktMaxLen uint32 = 12 + 233 ...
... ...

查看当前路径下包的导出元素的文档:

$go doc CmppActiveTestReqPktLen
package cmpp // import "."

const (
    CmppActiveTestReqPktLen uint32 = 12     //12d, 0xc
    CmppActiveTestRspPktLen uint32 = 12 + 1 //13d, 0xd
)
Packet length const for cmpp active test request and response packets.

我们看到包导出元素(比如CmppActiveTestReqPktLen)的头字母是大写的,go doc不会将其解析为包名,而会认为它是当前包中的某个元素。

通过-u选项,我们也可以查看当前路径下包的非导出元素的文档:

$go doc -u newPacketWriter
package cmpp // import "github.com/bigwhite/gocmpp"

func newPacketWriter(initSize uint32) *packetWriter

查看当前路径的子路径下的包的文档:

$go doc ./utils
或
$go doc utils

package cmpputils // import "github.com/bigwhite/gocmpp/utils"

var ErrInvalidUtf8Rune = errors.New("Not Invalid Utf8 runes")
func GB18030ToUtf8(in string) (string, error)
... ...
  • 查看项目依赖的第三方module的文档

如今,go module已经是Go依赖管理的标准模式了。一个项目依赖的go module会被cache到go mod专有路径中,包含不同版本和其代码。因此,目前go doc在查看项目依赖的第三方module的文档时,会自动到go mod cache中找到该module,并显示其文档,例如:

$go doc github.com/lni/dragonboat/v3
package dragonboat // import "github.com/lni/dragonboat/v3"

Package dragonboat is a multi-group Raft implementation.

The NodeHost struct is the facade interface for all features provided by the
dragonboat package. Each NodeHost instance usually runs on a separate host
managing its CPU, storage and network resources. Each NodeHost can manage Raft
nodes from many different Raft groups known as Raft clusters. Each Raft cluster
is identified by its ClusterID and it usually consists of multiple nodes,
each identified its NodeID value. Nodes from the same Raft cluster can be
considered as replicas of the same data, they are suppose to be distributed on
different NodeHost instances across the network, this brings fault tolerance to
machine and network failures as application data stored in the Raft cluster will
be available as long as the majority of its managing NodeHost instances (i.e.
its underlying hosts) are available.

... ...

const DragonboatMajor = 3 ...
var ErrClosed = errors.New("dragonboat: closed") ...
var ErrInvalidOperation = errors.New("invalid operation") ...
var ErrBadKey = errors.New("bad key try again later") ...
var ErrNoSnapshot = errors.New("no snapshot available") ...
func IsTempError(err error) bool
func WriteHealthMetrics(w io.Writer)
type ClusterInfo struct{ ... }
type GossipInfo struct{ ... }
type INodeUser interface{ ... }
type Membership struct{ ... }
type NodeHost struct{ ... }
    func NewNodeHost(nhConfig config.NodeHostConfig) (*NodeHost, error)
type NodeHostInfo struct{ ... }
type NodeHostInfoOption struct{ ... }
    var DefaultNodeHostInfoOption NodeHostInfoOption
type RequestResult struct{ ... }
type RequestResultCode int
type RequestState struct{ ... }
type SnapshotOption struct{ ... }
    var DefaultSnapshotOption SnapshotOption
type SysOpState struct{ ... }
type Target = string

如果要查看的依赖的module尚未get到本地,那么go doc会提示你先go get。

在传统gopath模式下,go doc则会自动到\$GOPATH下面查找对应的包路径,如果该包存在,就可以输出该包的相关文档。因此我们可以在任意路径下通过go doc查看第三方项目包的文档:

$export GO111MODULE=off
$go doc github.com/bigwhite/gocmpp.CmppActiveTestReqPktLen
package cmpp // import "github.com/bigwhite/gocmpp"

const (
    CmppActiveTestReqPktLen uint32 = 12     //12d, 0xc
    CmppActiveTestRspPktLen uint32 = 12 + 1 //13d, 0xd
)
    Packet length const for cmpp active test request and response packets.
  • 查看源码

如果要查看包的源码,我们没有必要将目录切换到该包所在路径并通过编辑器打开源文件查看,通过go doc我们一样可以查看包的完整源码或包的某元素的源码。

查看标准库包源码:

$go doc -src fmt.Printf
package fmt // import "fmt"

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func Printf(format string, a ...interface{}) (n int, err error) {
    return Fprintf(os.Stdout, format, a...)
}

查看当前路径包中导出元素的源码:

$go doc -src NewClient
package cmpp // import "."

// New establishes a new cmpp client.
func NewClient(typ Type) *Client {
    return &Client{
        typ: typ,
    }
}

查看当前路径包中未导出元素的源码:

$go doc -u -src newPacketWriter
package cmpp // import "github.com/bigwhite/gocmpp"

func newPacketWriter(initSize uint32) *packetWriter {
    buf := make([]byte, 0, initSize)
    return &packetWriter{
        wb: bytes.NewBuffer(buf),
    }
}

查看当前项目依赖的第三方包的某个函数的源码:

$go doc -src github.com/lni/dragonboat/v3 IsTempError
package dragonboat // import "github.com/lni/dragonboat/v3"

// IsTempError returns a boolean value indicating whether the specified error
// is a temporary error that worth to be retried later with the exact same
// input, potentially on a more suitable NodeHost instance.
func IsTempError(err error) bool {
    return err == ErrSystemBusy ||
        err == ErrClusterClosed ||
        err == ErrClusterNotInitialized ||
        err == ErrClusterNotReady ||
        err == ErrTimeout ||
        err == ErrClosed
}

go doc是原生工具,也非常强大,但是go doc是cli工具,不是能满足所有人的“口味”,那么小伙伴们可能会问:是否有godoc那样的离线web文档中心的替代工具呢?我们接下来就来聊聊pkgsite

三. pkgsite

Go官方推出新包文档站点后,在使用体验上的确有不少改善,新增了很多功能,下面是io包的在新包文档站点下的呈现形式:

Go老版官方站点与godoc是匹配的,同样,Go在推出新版Go包文档站点后,也开源了其站点源码,这个项目就是pkgsite。我们可以通过下面命令安装pkgsite:

$go install golang.org/x/pkgsite/cmd/pkgsite@latest

和godoc一样,pkgsite支持local mode,即离线模式。我们在某个go module下面(这里在gocmpp module的本地路径下)执行下面命令即可:

$pkgsite
2023/03/16 23:26:37 Info: go/packages.Load(["all"]) loaded 247 packages from . in 3.762976863s
2023/03/16 23:26:37 Info: Listening on addr http://localhost:8080

我们看到pkgsite加载了“all”范围的所有包以及当前module的包。打开浏览器,输入localhost:8080,便可以打开pkgsite服务的首页:

注:通过go help packages查看all的含义

搜索你要的包,得到列表后,打开包的详情页面,其展示形式与官方pkg.go.dev是一模一样的。

不过目前pkgsite在local模式下查看标准库包是有问题的,页面无法打开。

总体感觉pkgsite目前主要还是以满足官方站点在线文档查看需求为主,对local模式的支持不是很好,用起来也较为晦涩,这里也有gopher抱怨,希望能重新恢复godoc工具,但估计Go官方肯定不会答应,毕竟不想维护两套展示风格不同的工具。pkgsite后续可能会有改善,但目前看来优先级似乎不高。

四. 小结

日常开发工作中,我们总是online的,通过pkg.go.dev的在线文档可以满足绝大部分需求。

如果真是处于离线状态,我个人建议你的开发机上至少要将godoc、pkgsite都装上。对于习惯了godoc的gopher而言,虽然godoc已“作废”,但Go基于注释的文档兼容性不错,godoc依然可以满足初步的离线文档查看需求。如果你已经喜欢上Go新站点的风格,对新站点功能有依赖,那么pkgsite也是可以使用的。再辅以go doc命令行工具,离线查看文档需求也能满足个七七八八。

注:如果你使用的是像goland这样的IDE工具,其内置离线文档功能可能就会满足你的需求。

Go社区也有一些的第三方的离线go文档工具,比如貘兄(go101)golds也是不错的。


“Gopher部落”知识星球旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!2023年,Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码,关注代码质量并深入理解Go核心技术,并继续加强与星友的互动。欢迎大家加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

一文搞懂Go subtest

注:本篇首图片基于lexica AI生成的图片二次加工而成。

本文永久链接 – https://tonybai.com/2023/03/15/an-intro-of-go-subtest

单元测试(unit testing)是软件开发中至关重要的一环,它存在的意义包括但不限于如下几个方面:

  • 提高代码质量:单元测试可以确保代码的正确性、可靠性和稳定性,从而减少代码缺陷和bug。
  • 减少回归测试成本:在修改代码时,单元测试可以快速检查是否影响了其他模块的功能,避免了整个系统的回归测试成本。
  • 促进团队合作:单元测试可以帮助团队成员更好地理解和使用彼此编写的代码,提高代码的可读性和可维护性。
  • 提高开发效率:单元测试可以自动化执行测试,减少手动测试的时间和工作量,从而提高开发效率。

Go语言设计者在Go设计伊始就决定语言特性与环境特性“两手都要抓,两手都要硬”,事实证明:Go的成功正是因为其对工程软件项目整体环境的专注。而Go内置轻量级测试框架这一点也正是Go重视环境特性的体现。并且,Go团队对这一内置测试框架的投入是持续的,不断有更便捷的、更灵活的新特性加入Go测试框架中,可以帮助Gopher更好地组织测试代码,更高效地执行测试等。

Go在Go 1.7版本引入的subtest就是一个典型的代表,subtest的加入使得Gopher可以更灵活地应用内置go test框架。

在本文中,我将结合日常开发中了解到的关于subtest的认知、理解和使用的问题,和大家一起聊聊subtest。

一. Go单元测试回顾

在Go语言中,单元测试被视为一等公民,结合Go内置的轻量级测试框架,Go开发者可以很方便的编写单元测试用例。

Go的单元测试通常放在与被测试代码相同的包中,单元测试所在源文件以_test.go结尾,这个Go测试框架要求的。测试函数以Test为前缀,接受一个*testing.T类型的参数,并使用t.Error、t.Fail以及t.Fatal等方法来报告测试失败。使用go test命令即可运行所有的测试代码。如果测试通过,则输出一条消息表示测试成功;否则输出错误信息,指出哪些测试失败了。

注:Go还支持基准测试、example测试、模糊测试等,以便进行性能测试和文档生成,但这些不是这篇文章所要关注的内容。
注:t.Error <=> t.Log+t.Fail

通常编写Go测试代码时,我们首先会考虑top-level test。

二. Go top-level test

上面提到的与被测源码在相同目录下的*_test.go中的以Test开头的函数就是Go top-level test。在*_test.go可以定义一个或多个以Test开头的函数用于测试被测源码中函数或方法。例如:

// https://github.com/bigwhite/experiments/blob/master/subtest/add_test.go

// 被测代码,仅是demo
func Add(a, b int) int {
    return a + b
}

// 测试代码
func TestAdd(t *testing.T) {
    got := Add(2, 3)
    if got != 5 {
        t.Errorf("Add(2, 3) got %d, want 5", got)
    }
}

func TestAddZero(t *testing.T) {
    got := Add(2, 0)
    if got != 2 {
        t.Errorf("Add(2, 0) got %d, want 2", got)
    }
}

func TestAddOppositeNum(t *testing.T) {
    got := Add(2, -2)
    if got != 0 {
        t.Errorf("Add(2, -2) got %d, want 0", got)
    }
}

注:“got-want”是Go test中在Errorf中常用的命名惯例

top-level test的执行有如下特点:

  • go test会将每个TestXxx放在单独的goroutine中执行,保持相互的隔离
  • 某个TestXxx用例未过,通过Errorf,甚至是Fatalf输出错误结果,都不会影响到其他TestXxx的执行;
  • 某个TestXxx用例中的某个结果判断未过,如果通过Errorf输出错误结果,则该TestXxx会继续执行;
  • 不过,如果TestXxx使用的是Fatal/Fatalf,这会导致该TestXxx的执行在调用Fatal/Fatalf的位置立即结束,TestXxx函数体内的后续测试代码将不会得到执行;
  • 默认各个TestXxx按声明顺序逐一执行,即便它们是在各自的goroutine中执行的;
  • 通过go test -shuffle=on可以让各个TestXxx按随机次序执行,这样可以检测出各个TestXxx之间是否存在执行顺序的依赖,我们要避免在测试代码中出现这种依赖;
  • 通过“go test -run=正则式”的方式,可以选择执行某些TestXxx。
  • 各个TestXxx函数可以调用t.Parallel方法(即testing.T.Parallel方法)来将TestXxx加入到可并行执行的用例集合中,注意:加入到并行执行集合后,这些TestXxx的执行顺序就不确定了。

结合属于Go最佳实践的表驱动(table-driven)测试(如下面代码TestAddWithTable所示),我们可以无需写很多TestXxx,用下面的TestAddWithTable即可实现上面三个TestXxx的等价测试:

func TestAddWithTable(t *testing.T) {
    cases := []struct {
        name string
        a    int
        b    int
        r    int
    }{
        {"2+3", 2, 3, 5},
        {"2+0", 2, 0, 2},
        {"2+(-2)", 2, -2, 0},
        //... ...
    }

    for _, caze := range cases {
        got := Add(caze.a, caze.b)
        if got != caze.r {
            t.Errorf("%s got %d, want %d", caze.name, got, caze.r)
        }
    }
}

Go top-level test可以满足大多数Gopher的常规单测需求,表驱动的惯例理解起来也十分容易。

但基于top-level test+表驱动的测试在简化测试代码编写的同时,也会带来一些不足:

  • 表内的cases顺序执行,无法shuffle;
  • 表内所有cases在同一个goroutine中执行,隔离性差;
  • 如果使用fatal/fatalf,那么一旦某个case失败,后面的测试表项(cases)将不能得到执行;
  • 表内test case无法并行(parallel)执行;
  • 测试用例的组织只能平铺,不够灵活,无法建立起层次。

为此Go 1.7版本引入了subtest!

三. Subtest

Go语言的subtest是指将一个测试函数(TestXxx)分成多个小测试函数,每个小测试函数可以独立运行并报告测试结果的功能。这种测试方式可以更细粒度地控制测试用例,方便定位问题和调试。

下面是一个使用subtest改造TestAddWithTable的示例代码,展示如何使用Go语言编写subtest:

// https://github.com/bigwhite/experiments/blob/master/subtest/add_sub_test.go

func TestAddWithSubtest(t *testing.T) {
    cases := []struct {
        name string
        a    int
        b    int
        r    int
    }{
        {"2+3", 2, 3, 5},
        {"2+0", 2, 0, 2},
        {"2+(-2)", 2, -2, 0},
        //... ...
    }

    for _, caze := range cases {
        t.Run(caze.name, func(t *testing.T) {
            t.Log("g:", curGoroutineID())
            got := Add(caze.a, caze.b)
            if got != caze.r {
                t.Errorf("got %d, want %d", got, caze.r)
            }
        })
    }
}

在上面的代码中,我们定义了一个名为TestAddWithSubtest的测试函数,并在其中使用t.Run()方法结合表测试方式来创建三个subtest,这样每个subtest都可以复用相同的错误处理逻辑,但通过测试用例参数的不同来体现差异。当然你若不使用表驱动测试,那么每个subtest也都可以有自己独立的错误处理逻辑!

执行上面TestAddWithSubtest这个测试用例(我们故意将Add函数的实现改成错误的),我们将看到下面结果:

$go test  add_sub_test.go
--- FAIL: TestAddWithSubtest (0.00s)
    --- FAIL: TestAddWithSubtest/2+3 (0.00s)
        add_sub_test.go:54: got 6, want 5
    --- FAIL: TestAddWithSubtest/2+0 (0.00s)
        add_sub_test.go:54: got 3, want 2
    --- FAIL: TestAddWithSubtest/2+(-2) (0.00s)
        add_sub_test.go:54: got 1, want 0

我们看到:在错误信息输出中,每个失败case都是以“TestXxx/subtestName”标识,我们可以很容易地将其与相应的代码行对应起来。更深层的意义是subtest让整个测试组织形式有了“层次感”!通过-run标志位,我们便能够以这种“层次”选择要执行的某个top-level test的某个/某些Subtest:

$go test  -v -run TestAddWithSubtest/-2 add_sub_test.go
=== RUN   TestAddWithSubtest
=== RUN   TestAddWithSubtest/2+(-2)
    add_sub_test.go:51: g: 19
    add_sub_test.go:54: got 1, want 0
--- FAIL: TestAddWithSubtest (0.00s)
    --- FAIL: TestAddWithSubtest/2+(-2) (0.00s)
FAIL
FAIL    command-line-arguments  0.006s
FAIL

我们来看看subtest有哪些特点(可以和前面的top-level test对比着看):

  • go subtest也会放在单独的goroutine中执行,保持相互的隔离
  • 某个Subtest用例未过,通过Errorf,甚至是Fatalf输出错误结果,都不会影响到同一TestXxx下的其他Subtest的执行;
  • 某个Subtest中的某个结果判断未过,如果通过Errorf输出错误结果,则该Subtest会继续执行;
  • 不过,如果subtest使用的是Fatal/Fatalf,这会导致该subtest的执行在调用Fatal/Fatalf的位置立即结束,subtest函数体内的后续测试代码将不会得到执行;
  • 默认各个TestXxx下的subtest将按声明顺序逐一执行,即便它们是在各自的goroutine中执行的;
  • 到目前为止,subtest不支持shuffle方式的随机序执行
  • 通过“go test -run=TestXxx/正则式[/...]”的方式,我们可以选择执行TestXxx下的某个或某些subtest;
  • 各个subtest可以调用t.Parallel方法(即testing.T.Parallel方法)来将subtest加入到可并行执行的用例集合中,注意:加入到并行执行集合后,这些subTest的执行顺序就不确定了。

综上,subtest的优点可以总结为以下几点:

  • 更细粒度的测试:通过将测试用例分成多个小测试函数,可以更容易地定位问题和调试。
  • 可读性更好:subtest可以让测试代码更加清晰和易于理解。
  • 更灵活的测试:subtest可以根据需要进行组合和排列,以满足不同的测试需求。
  • 更有层次的组织测试代码:通过subtest可以设计出更有层次的测试代码组织形式,更方便地共享资源和在某一组织层次上设置setup与teardown,我的《Go语言精进之路》vol2的第41条“有层次地组织测试代码”对这方面内容有系统说明,大家可以参考。

四. Subtest vs. top-level test

top-level test自身其实也是一种subtest,只是在它的调度与执行是由Go测试框架掌控的的,对我们开发人员并不可见。

对于gopher而言:

  • 简单的包的测试在top-level test中就可以满足,直接、直观、易懂。
  • 对于稍大一些的工程中的复杂包来说,一旦涉及到测试代码组织的层次设计时,subtest的组织性、灵活性和可扩展性便可以更好地的帮助我们提高测试效率和减少测试时间。

注:本文少部分内容来自于ChatGPT生成的答案。

本文涉及的源码可以在这里下载。


“Gopher部落”知识星球旨在打造一个精品Go学习和进阶社群!高品质首发Go技术文章,“三天”首发阅读权,每年两期Go语言发展现状分析,每天提前1小时阅读到新鲜的Gopher日报,网课、技术专栏、图书内容前瞻,六小时内必答保证等满足你关于Go语言生态的所有需求!2023年,Gopher部落将进一步聚焦于如何编写雅、地道、可读、可测试的Go代码,关注代码质量并深入理解Go核心技术,并继续加强与星友的互动。欢迎大家加入!

img{512x368}
img{512x368}

img{512x368}
img{512x368}

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

  • 微博(暂不可用):https://weibo.com/bigwhite20xx
  • 微博2:https://weibo.com/u/6484441286
  • 博客:tonybai.com
  • github: https://github.com/bigwhite

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats