bitfield/gotestdox
GitHub: bitfield/gotestdox
将 Go 测试的驼峰命名转换为可读的自然语言句子,让测试结果输出像行为文档一样清晰易懂。
Stars: 200 | Forks: 5
[](https://pkg.go.dev/github.com/bitfield/gotestdox)
[](https://goreportcard.com/report/github.com/bitfield/gotestdox)
[](https://github.com/avelino/awesome-go)



`gotestdox` 是一个命令行工具,用于将 Go 测试结果格式化为可读的文档,正如我的书 [The Power of Go: Tests](https://bitfieldconsulting.com/books/tests) 中所推荐的那样。
以下是安装方法:
```
go install github.com/bitfield/gotestdox/cmd/gotestdox@latest
```
在任何 Go 项目中,运行:
```
gotestdox ./...
```

# 它能做什么?
`gotestdox` 会运行你的测试并报告结果,但它以一种特殊的方式格式化测试名称。它会将 WrittenInCamelCase(驼峰命名法)的测试名称转换为普通的句子。
例如,假设我们有一些这样命名的测试:
```
TestValidIsTrueForValidInputs
TestValidIsFalseForInvalidInputs
```
通过运行 `gotestdox`,我们可以将它们转换为表达预期行为且易于阅读的句子:
**`gotestdox`**
这将运行测试,并打印:
```
✔ Valid is true for valid inputs (0.00s)
✔ Valid is false for invalid inputs (0.00s)
```
# 为什么?
我读了 Dan North 的一篇博客文章,里面说道:
# 如何实现?
最初的 [`testdox`](https://github.com/astubbs/testdox) 工具(`agiledox` 的一部分)非常简单,正如 Dan 所描述的:它只是将像 `testFailsForDuplicateCustomers` 这样的驼峰式 JUnit 测试名称变成了像 `fails for duplicate customers` 这样用空格分隔的句子。
这就是我觉得它很棒的地方:它是如此简单,似乎没有什么价值,但实际上并非如此。我已经利用这个想法改进了我的许多测试名称。
除了 Java,`testdox` 还有针对其他各种语言的实现:例如 [PHP](https://phpunit.readthedocs.io/en/9.5/textui.html#testdox)、[Python](https://pypi.org/project/pytest-testdox/) 和 [.NET](https://testdox.wordpress.com/)。我还没有找到 Go 的实现版本,所以它来了。
# 进阶用法
使用 `gotestdox` 的一些更高级的方法:
## 退出状态
如果有测试失败,`gotestdox` 将打印出失败测试的输出信息,并在退出时报告状态 1。
## 颜色
`gotestdox` 使用 `✔`(复选标记 emoji)表示测试通过,使用 `x` 表示测试失败。它们分别以绿色和红色显示,这使用了 [`color`](https://github.com/fatih/color) 库,该库会自动检测是否连接到了支持颜色的终端。
如果不是这样(例如,当你将输出重定向到文件时),或者如果设置了 [`NO_COLOR`](https://no-color.org/) 环境变量(不论为何值),颜色输出将被禁用。
## 测试 flags 和参数
没有任何参数的 `gotestdox` 将运行命令 `go test -json` 并处理其输出。
你提供的任何参数都将传递给 `go test`。例如:
**`gotestdox -run ParseJSON`**
将运行命令:
`go test -json -run ParseJSON`
你可以提供要测试的包列表,或者 `go test` 支持的任何其他参数或 flags。但是,`gotestdox` 只打印关于*测试*的事件(忽略 benchmark 和示例)。
由于 fuzz 测试用例是自动生成的,且其名称往往没有太大用处,因此除非测试失败,否则它们不会包含在 `gotestdox` 的输出中。
## 多个包
要测试当前目录树中的所有包,请运行:
**`gotestdox ./...`**
每个包的测试结果前面都会加上该包的完全限定名称。例如:
```
github.com/octocat/mymodule/api:
✔ NewServer errors on invalid config options (0.00s)
✔ NewServer returns a correctly configured server (0.00s)
github.com/octocat/mymodule/util:
x LeftPad adds the correct number of leading spaces (0.00s)
util_test.go:133: want " dummy", got " dummy"
```
## 多词函数名
对于名称包含多个单词的函数,其测试名称会存在一些歧义。例如,假设我们正在测试一个名为 `HandleInput` 的函数,我们编写了这样一个测试:
```
TestHandleInputClosesInputAfterReading
```
如果不做任何处理,它将被渲染为:
```
✔ Handle input closes input after reading
```
为了让我们能给 `gotestdox` 一个关于此情况的提示,这里有一个额外的转换规则:第一个下划线标记函数名的结束。因此,我们可以像这样命名我们的测试:
```
TestHandleInput_ClosesInputAfterReading
```
这样就会变成:
```
✔ HandleInput closes input after reading
```
我认为这是一个可以接受的折中方案:`gotestdox` 的输出可读性大大提高,而测试名称中额外的下划线并不会严重干扰其本身的阅读。
无论如何,我们的目的并不是将所有合理的测试名称都*完美地*渲染成句子,而是对它们做一些*有用*的处理,主要是为了鼓励开发者编写能够提供有用信息、描述单元行为的测试名称,从而(作为附带效果)在被 `gotestdox` 格式化时具有良好的可读性。
换句话说,`gotestdox` 本身并不是目的。它是引导我们达成目标的手段,最终目标是有意义的测试名称(我喜欢用_富有文采_的测试名称来形容)。
## 过滤标准输入
如果你想自己运行 `go test -json`(例如作为 shell 流水线的一部分),并将其输出通过管道传递给 `gotestdox`,你完全可以这么做:
**`go test -json | gotestdox`**
在这种情况下,传递给 `gotestdox` 的任何 flags 或参数都将被忽略,并且它不会去*运行*测试;相反,它将纯粹作为一个文本过滤器发挥作用。但是,就像它自己运行测试时一样,如果有测试失败,它也会报告退出状态 1。
## 作为包使用
请参阅 [pkg.go.dev/github.com/bitfield/gotestdox](https://pkg.go.dev/github.com/bitfield/gotestdox) 获取在你自己的程序中将 `gotestdox` 作为包使用的完整文档。
# 那又怎样?
那你为什么要关注它呢?我发现,`gotestdox` 或任何类似 `testdox` 的工具的有趣之处在于,它的输出会让你思考你的测试、你如何命名它们,以及它们到底做了什么。
正如 Dan 在他的博客文章中所说,将测试名称转换为句子是一个非常简单的想法,但它具有强大的效果。测试名称*应该*是句子。
## 测试名称应该是句子
我不知道你的情况,但多年来,我在尝试为测试选择好名字上浪费了大量的时间和精力。我那时真的没有一种方法来评估我选择的名字到底好不好。现在有了!
事实上,我写了一整篇关于这个的博客文章:
* [测试名称应该是句子](https://bitfieldconsulting.com/golang/test-names)
把你的 `gotestdox` 输出展示给用户、客户或业务人员,看看对他们来说是否有意义,这可能会很有趣。如果是这样的话,你就找对方向了。而且这很可能会引发一些有趣的对话(“它真的是做这个的吗?但这根本不是我们要求的!”)
看来我不是唯一一个觉得这个想法很有用的人。我听说 `gotestdox` 已经在一些相当大型的 Go 项目和公司中使用,帮助他们的开发者从现有的测试中获得更多价值,并鼓励他们以有趣的新方式思考测试的真正用途。真是太棒了!
# 链接
Gopher 图像作者 [MariaLetta](https://github.com/MariaLetta/free-gophers-pack)
标签:EVTX分析, Homebrew安装, 文档结构分析, 日志审计