This commit is contained in:
chai2010
2015-12-16 10:55:14 +08:00
parent 3e80d25c15
commit c187ed90f8
6 changed files with 60 additions and 41 deletions

View File

@@ -1,47 +1,60 @@
### 11.2.4. 擴展測試包
虑下这两个包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服和HTTP客端的功能. 如我所料, 上的 net/http 包依赖下层的 net/url 包. 然, net/url 包中的一个测试是演示不URL和HTTP客端的交互行. 也就是, 一个下层包的测试代码导入了上的包.
慮下這兩個包: net/url 包, 提供了 URL 解析的功能; net/http 包, 提供了web服和HTTP客端的功能. 如我所料, 上的 net/http 包依賴下層的 net/url 包. 然, net/url 包中的一個測試是演示不URL和HTTP客端的交互行. 也就是, 一個下層包的測試代碼導入了上的包.
![](../images/ch11-01.png)
这样的行在 net/url 包的测试代码中会导致包的循环依赖, 正如 11.1中上箭所示, 同时正如我在 10.1节所说, Go语言规范是禁止包的循环依赖的.
這樣的行在 net/url 包的測試代碼中會導緻包的循環依賴, 正如 11.1中上箭所示, 衕時正如我在 10.1節所説, Go語言規範是禁止包的循環依賴的.
可以通过测试扩展包的方式解决循环依赖的问题, 也就是在 net/url 包所在的目录声明一 url_test 测试扩展包. 其中测试扩展包名的 `_test` 后缀告诉 go test 工具它应该建立一个额外的包来运行测试. 我们将这个扩展测试包的入路径视作是 net/url_test 更容易理解, 但实际上它不能被其他任何包入.
可以通過測試擴展包的方式解決循環依賴的問題, 也就是在 net/url 包所在的目録聲明一 url_test 測試擴展包. 其中測試擴展包名的 `_test` 後綴告訴 go test 工具它應該建立一個額外的包來運行測試. 我們將這個擴展測試包的入路徑視作是 net/url_test 更容易理解, 但實際上它不能被其他任何包入.
为测试扩展包是一个独立的包, 因此可以导入测试代码依赖的其他的助包; 包内的测试代码可能法做到. 在设计层面, 测试扩展包是在所以它依的包的上, 正如 11.2所示.
為測試擴展包是一個獨立的包, 因此可以導入測試代碼依賴的其他的助包; 包內的測試代碼可能法做到. 在設計層麪, 測試擴展包是在所以它依的包的上, 正如 11.2所示.
![](../images/ch11-02.png)
过回避循环导入依, 扩展测试包可以更活的测试, 特是集成测试(需要测试多个组件之的交互), 可以像普通用程序那自由地入其他包.
過迴避循環導入依, 擴展測試包可以更活的測試, 特是集成測試(需要測試多個組件之的交互), 可以像普通用程序那自由地入其他包.
可以用 go list 工具看包对应目录中哪些Go源文件是品代, 哪些是包内测试, 哪些测试扩展包. 我以 fmt 包作为一个例子. GoFiles 表示产品代码对应的Go源文件列; 也就是 go build 命令要编译的部分:
可以用 go list 工具看包對應目録中哪些Go源文件是品代, 哪些是包內測試, 哪些測試擴展包. 我以 fmt 包作為一個例子. GoFiles 錶示產品代碼對應的Go源文件列; 也就是 go build 命令要編譯的部分:
{% raw %}
```
$ go list -f={{.GoFiles}} fmt
[doc.go format.go print.go scan.go]
```
TestGoFiles 表示的是 fmt 包内部测试测试代码, 以 _test.go 为后缀文件名, 不过只在测试时被构建:
{% endraw %}
TestGoFiles 錶示的是 fmt 包內部測試測試代碼, 以 _test.go 為後綴文件名, 不過隻在測試時被構建:
{% raw %}
```
$ go list -f={{.TestGoFiles}} fmt
[export_test.go]
```
包的测试代码通常都在这些文件中, 不过 fmt 包并非如此; 稍后我们再解释 export_test.go 文件的作用.
{% endraw %}
XTestGoFiles 表示的是属于测试扩展包的测试代码, 也就是 fmt_test 包, 因此它们必须先导入 fmt 包. 同样, 这些文件也只是在测试时被构建运行:
包的測試代碼通常都在這些文件中, 不過 fmt 包並非如此; 稍後我們再解釋 export_test.go 文件的作用.
XTestGoFiles 錶示的是屬於測試擴展包的測試代碼, 也就是 fmt_test 包, 因此它們必鬚先導入 fmt 包. 衕樣, 這些文件也隻是在測試時被構建運行:
{% raw %}
```
$ go list -f={{.XTestGoFiles}} fmt
[fmt_test.go scan_test.go stringer_test.go]
```
有时候测试扩展包需要访问被测试包内部的代码, 例如在一个为了避免循环导入而被独立到外部测试扩展包的白盒测试. 在这种情况下, 我们可以通过一些技巧解决: 我们在包内的一个 _test.go 文件中导出一个内部的实现给测试扩展包. 因为这些代码只有在测试时才需要, 因此一般放在 export_test.go 文件中.
{% endraw %}
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函数提供的功能. 但是为了避免太多的依赖, fmt 包并没有导入包含巨大表格数据的 unicode 包; 相反fmt包有一个叫 isSpace 内部的简易实现.
有時候測試擴展包需要訪問被測試包內部的代碼, 例如在一個為了避免循環導入而被獨立到外部測試擴展包的白盒測試. 在這種情況下, 我們可以通過一些技巧解決: 我們在包內的一個 _test.go 文件中導齣一個內部的實現給測試擴展包. 因為這些代碼隻有在測試時纔需要, 因此一般放在 export_test.go 文件中.
为了确保 fmt.isSpace 和 unicode.IsSpace 函数的行为一致, fmt 包谨慎地包含了一个测试. 是一个在测试扩展包内的测试, 因此是无法直接访问到 isSpace 内部函数的, 因此 fmt 通过一个秘密出口导出了 isSpace 函数. export_test.go 文件就是专门用于测试扩展包的秘密出口.
例如, fmt 包的 fmt.Scanf 需要 unicode.IsSpace 函數提供的功能. 但是為了避免太多的依賴, fmt 包並沒有導入包含鉅大錶格數據的 unicode 包; 相反fmt包有一個叫 isSpace 內部的簡易實現.
為了確保 fmt.isSpace 和 unicode.IsSpace 函數的行為一緻, fmt 包謹慎地包含了一個測試. 是一個在測試擴展包內的測試, 因此是無法直接訪問到 isSpace 內部函數的, 因此 fmt 通過一個祕密齣口導齣了 isSpace 函數. export_test.go 文件就是專門用於測試擴展包的祕密齣口.
```Go
package fmt
@@ -49,5 +62,5 @@ package fmt
var IsSpace = isSpace
```
这个测试文件并没有定义测试代码; 它是通 fmt.IsSpace 简单导出了内部的 isSpace 函, 提供给测试扩展包使用. 这个技巧可以广泛用于位于测试扩展包的白盒测试.
這個測試文件並沒有定義測試代碼; 它是通 fmt.IsSpace 簡單導齣了內部的 isSpace 函, 提供給測試擴展包使用. 這個技巧可以廣汎用於位於測試擴展包的白盒測試.