如何仅在测试时允许一个包访问另一个包的未导出数据?

How can I allow one package access to another package's unexported data only when testing?

Go 编程语言 的第 11.2.4 节中,有一个外部测试通过声明 IsSpace 访问 fmt.isSpace() 的示例在 fmtexport_test.go 文件中。这似乎是完美的解决方案,所以这就是我所做的:

a/a.go:

package a

var x int

func Set(v int) {
    x = v
}

a/a_test.go:

package a
import "testing"

func TestSet(t *testing.T) {
    Set(42)
    if x != 42 {
        t.Errorf("x == %d (expected 42)", x)
    }
}

func Get() int {
    return x
}

(运行 go testa/ 中工作正常。)

b/b.go:

package b
import "path/to/a"

func CallSet() {
    a.Set(105)
}

b/b_test.go

package b
import (
    "testing"
    "path/to/a"
)

func TestCallSet(t *testing.T) {
    CallSet()
    if r := a.Get(); r != 105 {
        t.Errorf("Get() == %d (expected 105)", r)
    }
}

不幸的是,当我在 b/ 中 运行 go test 时,我得到:

./b_test.go:11: undefined: a.Get

尝试 运行 同时进行两组测试 go test ./... 没有帮助。

经过相当多的探索后,我发现“The *_test.go files are compiled into the package only when running go test for that package”(强调我的)。 (因此,换句话说,我可以从 a/ 中的 a_test 外部测试包访问 a.Get,但不能从 a/ 之外的任何包访问 a.Get。)

有没有其他方法可以让一个包的测试访问另一个包的其他内部数据,以进行集成测试?

Is there some other way I can allow tests from one package to access otherwise-internal data from another package, for integration testing purposes?

没有。没有。

如前所述,无法"grant"访问未导出的标识符。

虽然需要/证明对 fmt 包测试的一些说明。

有两种测试:黑盒测试和白盒测试。

黑盒 测试是将包视为 "black-box",并且仅通过其导出的标识符(通过其 "public API" ,其他包看到的)。在这种情况下,测试文件具有不同的包名称(例如,测试 fmt 包时 fmt_test)。

白盒 测试是指同时使用包的导出和未导出标识符。要创建白盒测试,您在测试文件中指定与被测试包相同的包名称(因此,fmtfmt 包测试的情况下)。

标准库的 fmt 包中包含的测试 打算 成为黑盒测试,但那样就无法测试所有内容。所以 Go 的作者选择了 mixed 版本:他们包含了一个 export_test.go 测试文件,它使用相同的包声明(package fmt)所以它可以访问未导出的fmt 包的标识符,以及 "exports" 2 个标识符,以便其他(黑盒)测试文件可以访问:

var IsSpace = isSpace
var Parsenum = parsenum

作者这样做是因为他们想尽量减少使用未导出的标识符,所以这明确标记了使用哪些未导出的标识符,并且基本上充当 "bridge" 之间的 [=] 11=] 包和 fmt 包的黑盒测试。

这里要注意的一件事是,这些只会 "exported" 到 fmt 包的(黑盒)测试(和 fmt 的白盒测试当然),而不是其他包或其他包的测试。原因很简单,因为构建包时不会解析和编译测试文件,只有当包测试是 运行.

解决方案?

未导出的标识符是针对包本身的,与其他人无关。这意味着没有其他包应该想要测试它们。如果需要对其进行测试,则必须在包自己的测试中完成。

如果对于集成测试您需要访问未导出的标识符,则包必须导出 "something" 以公开值(或其中的某些部分),或者在不导出敏感数据或实现的情况下帮助测试细节。如果包 API 设计良好且测试彻底,则永远(很少)需要它。