在Go语言中进行测试时,适当的包命名方式是什么?

huangapple go评论181阅读模式
英文:

Proper package naming for testing with the Go language

问题

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

策略1:

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

package myfunc

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

package myfunc

参考bzip2的示例。

策略2:

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

package myfunc

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

package myfunc_test

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

参考wire的示例。

策略3:

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

package myfunc

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

package myfunc_test

import (
    . "myfunc"
)

参考strings的示例。

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

英文:

I have seen several different test package naming strategies within Go and wanted to know what pros and cons of each are and which one I should use.

Strategy 1:

File name: github.com/user/myfunc.go

package myfunc

Test file name: github.com/user/myfunc_test.go

package myfunc

See bzip2 for an example.

Strategy 2:

File name: github.com/user/myfunc.go

package myfunc

Test file name: github.com/user/myfunc_test.go

package myfunc_test

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

See wire for an example.

Strategy 3:

File name: github.com/user/myfunc.go

package myfunc

Test file name: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

See strings for an example.

The Go standard library seems to use a mixture of strategy 1 and 2. Which of all three should I use? It's a pain appending package *_test to my testing packages as it means I can't test my package private methods but maybe there is a hidden advantage I am not aware of?

答案1

得分: 225

你列出的三种策略之间的根本区别在于测试代码是否与被测试的代码位于同一个包中。在测试文件中使用package myfuncpackage myfunc_test的决定取决于你是想进行白盒测试还是黑盒测试。

在一个项目中同时使用这两种方法是没有问题的。例如,你可以有myfunc_whitebox_test.gomyfunx_blackbox_test.go

测试代码包比较

  • 黑盒测试: 使用package myfunc_test,这将确保你只使用导出的标识符
  • 白盒测试: 使用package myfunc,这样你就可以访问非导出的标识符。适用于需要访问非导出变量、函数和方法的单元测试。

在问题中列出的策略比较

  • 策略1: 文件myfunc_test.go使用package myfunc — 在这种情况下,myfunc_test.go中的测试代码将与myfunc.go中的被测试代码位于同一个包中,即本例中的myfunc
  • 策略2: 文件myfunc_test.go使用package myfunc_test — 在这种情况下,myfunc_test.go中的测试代码将“被编译为一个单独的包,然后与主测试二进制文件链接并运行”。[来源:test.go源代码中的第58-59行]
  • 策略3: 文件myfunc_test.go使用package myfunc_test,但使用点符号导入myfunc — 这是策略2的一个变体,但使用点符号导入myfunc
英文:

The fundamental difference between the three strategies you've listed is whether or not the test code is in the same package as the code under test. The decision to use package myfunc or package myfunc_test in the test file depends on whether you want to perform white-box or black-box testing.

There's nothing wrong with using both methods in a project. For instance, you could have myfunc_whitebox_test.go and myfunx_blackbox_test.go.

Test Code Package Comparison

  • Black-box Testing: Use package myfunc_test, which will ensure you're only using the exported identifiers.
  • White-box Testing: Use package myfunc so that you have access to the non-exported identifiers. Good for unit tests that require access to non-exported variables, functions, and methods.

Comparison of Strategies Listed in Question

  • Strategy 1: The file myfunc_test.go uses package myfunc — In this case the test code in myfunc_test.go will be in the same package as the code being tested in myfunc.go, which is myfunc in this example.
  • Strategy 2: The file myfunc_test.go uses package myfunc_test — In this case the test code in myfunc_test.go "will be compiled as a separate package, and then linked and run with the main test binary." [Source: Lines 58–59 in the test.go source code]
  • Strategy 3: The file myfunc_test.go uses package myfunc_test but imports myfunc using the dot notation — This is a variant of Strategy 2, but uses the dot notation to import myfunc.

答案2

得分: 25

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

如果你有一个包含许多内部内容需要进行测试的大型包,那么可以在同一个包中进行测试。但这并不意味着你的测试可以访问任何私有状态。那样会使重构变得非常困难。当我在 Go 中编写结构体时,我经常实现接口。我从我的测试中调用的是这些接口方法,而不是单独调用所有的辅助方法/函数。

英文:

It depends on the scope of your tests. High level tests (integration, acceptance, etc...) should probably be placed in a separate package to ensure that you are using the package via the exported API.

If you have a large package with a lot of internals that need to be put under test then use the same package for your tests. But that's not an invitation for your tests to access any bit of private state. That would make refactoring a nightmare. When I write structs in go I am often implementing interfaces. It is those interface methods that I invoke from my tests, not all of the helper methods/functions individually.

答案3

得分: 13

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

英文:

You should use strategy 1 whenever possible. You can use the special foo_test package name to avoid import cycles, but that's mostly there so the standard library can be tested with the same mechanism. For example, strings cannot be tested with strategy 1 since the testing package depends on strings. As you said, with strategy 2 or 3 you don't have access to the package's private identifiers, so it's usually better to not use it unless you have to.

答案4

得分: 4

关于从Golang CodeReviewComments中导入import .,我想补充一些重要的说明:

import .形式在由于循环依赖而无法成为被测试包的一部分的测试中非常有用:

package foo_test

import (
	"bar/testutil" // 也导入了"foo"
	. "foo"
)

在这种情况下,测试文件不能属于foo包,因为它使用了导入foo的bar/testutil。所以我们使用import .形式让文件假装成foo包的一部分,即使它实际上并不是。

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

英文:

One important notes I'd like to add about import . from Golang CodeReviewComments:

The import . form can be useful in tests that, due to circular dependencies,
cannot be made part of the package being tested:

package foo_test

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

In this case, the test file cannot be in package foo because it uses
bar/testutil, which imports foo. So we use the 'import .' form to let the file
pretend to be part of package foo even though it is not.

Except for this one case, do not use import . in your programs.
It makes the programs much harder to read because it is unclear whether a name
like Quux is a top-level identifier in the current package or in an imported package.

huangapple
  • 本文由 发表于 2013年11月15日 17:56:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/19998250.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定