ChatGPT解决这个技术问题 Extra ChatGPT

使用 Go 语言进行测试的正确包命名

我在 Go 中看到了几种不同的测试包命名策略,想知道每种方法的优缺点以及我应该使用哪一种。

策略一:

文件名:github.com/user/myfunc.go

package myfunc

测试文件名:github.com/user/myfunc_test.go

package myfunc

有关示例,请参见 bzip2

策略二:

文件名:github.com/user/myfunc.go

package myfunc

测试文件名:github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

有关示例,请参见 wire

策略三:

文件名:github.com/user/myfunc.go

package myfunc

测试文件名:github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

有关示例,请参见 strings

Go 标准库似乎混合使用了策略 1 和 2。我应该使用这三个中的哪一个?将 package *_test 附加到我的测试包中很痛苦,因为这意味着我无法测试我的包私有方法,但也许有一个我不知道的隐藏优势?

这个问题只会导致不同的意见,但我会抛出我的。您不需要测试您的私有方法。您想测试其他开发人员将使用的包的接口。如果测试失败,那么您知道您的私有方法需要查看。
策略 2 的 [wire]( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go) 示例实际上现在也是策略 1 的示例...

M
Matthew Rankin

您列出的三种策略之间的根本区别在于测试代码是否与被测代码在同一个包中。在测试文件中使用 package myfunc 还是 package myfunc_test 的决定取决于您是要执行 white-box 还是 black-box 测试。

在项目中同时使用这两种方法没有任何问题。例如,您可以有 myfunc_whitebox_test.gomyfunx_blackbox_test.go

测试代码包比较

黑盒测试:使用包 myfunc_test,这将确保您只使用导出的标识符。

白盒测试:使用包 myfunc 以便您可以访问未导出的标识符。适用于需要访问非导出变量、函数和方法的单元测试。

问题中列出的策略比较

策略 1:文件 myfunc_test.go 使用包 myfunc — 在这种情况下,myfunc_test.go 中的测试代码将与 myfunc.go 中正在测试的代码在同一个包中,在本例中为 myfunc。

策略 2:文件 myfunc_test.go 使用包 myfunc_test — 在这种情况下,myfunc_test.go 中的测试代码“将被编译为单独的包,然后与主测试二进制文件链接并运行。” [来源:test.go 源代码中的第 58-59 行]

策略 3:文件 myfunc_test.go 使用包 myfunc_test 但使用点符号导入 myfunc — 这是策略 2 的变体,但使用点符号导入 myfunc。


应该注意的是,使用策略 1 还将使带有 _test.go 的文件与被测试的包分开(与策略 2 的行为相同)。这似乎没有按 github.com/golang/go/issues/15315 记录
我看到强包使用策略3,但我不明白这有什么意义?
我分叉了一个包并进行了更改,现在我的测试都在尝试导入原始存储库而不是我的分叉包。使用策略 3,我不必将“github.com/original/link”更改为“github.com/my/fork”,因为它只是引用 '.'反而。
@KevinDeenauth 这让我很惊讶。当我刚刚找到一个包含非 _test 包名称的 _test.go 时,我以为我发现了一个陷阱,该 func init() 更改了一些全局包变量以进行测试。我错了。
@nmarley . 无法解决您的分叉问题。这不是相对进口。它只是将标识符“导入当前包”。
m
mdwhatcott

这取决于您的测试范围。高级测试(集成、验收等)可能应该放在单独的包中,以确保您通过导出的 API 使用该包。

如果您有一个包含大量内部组件的大包需要进行测试,那么请使用相同的包进行测试。但这并不是邀请您的测试访问任何私有状态。那将使重构成为一场噩梦。 When I write structs in go 我经常实现接口。这是我从测试中调用的那些接口方法,而不是单独的所有辅助方法/函数。


g
guelfey

您应该尽可能使用策略 1。您可以使用特殊的 foo_test 包名称来避免导入循环,但这主要是因为可以使用相同的机制测试标准库。例如,无法使用策略 1 测试 strings,因为 testing 包依赖于 strings。正如您所说,使用策略 2 或 3 您无法访问包的私有标识符,因此通常最好不要使用它,除非您必须这样做。


在测试中无法访问私有标识符如何不是一种美德?
根据良好的测试实践,您不会测试代码工件的内部实现细节;做儿子,是代码味道
它并不总是一种代码气味。您可以使用最适合您的任何策略。
E
Eric

关于 Golang CodeReviewComments 中的 import .,我想补充一个重要说明:

import . 形式在由于 循环 依赖关系而不能成为被测试包的一部分的测试中很有用:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

在这种情况下,测试文件不能在包 foo 中,因为它使用 bar/testutil,它会导入 foo。所以我们使用'import'。形式让文件假装是包 foo 的一部分,即使它不是。

除了这一种情况,不要在您的程序中使用 import .。它使程序更难阅读,因为不清楚像 Quux 这样的名称是当前包中的顶级标识符还是导入包中的顶级标识符。