mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 08:36:37 +08:00
wavelet init
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# 可扩展验证 API
|
||||
|
||||
设计可重用测试验证函数的详细参考,调用者可以将其用于验收测试。来源:Google Go Style Guide(最佳实践)。
|
||||
|
||||
---
|
||||
|
||||
## `*test` 包导出模式
|
||||
|
||||
当你拥有一个由他人实现的接口时,在配套的 `*test` 包中导出一个验证函数。这使实现者无需复制你的测试逻辑即可验证正确性。
|
||||
|
||||
```go
|
||||
// Package storagetest 为 storage.Backend 提供验收测试。
|
||||
package storagetest
|
||||
|
||||
// Verify 对任何 storage.Backend 运行验证套件。
|
||||
// 返回描述第一个违规行为的错误,成功时返回 nil。
|
||||
func Verify(b storage.Backend) error {
|
||||
if err := verifyRoundTrip(b); err != nil {
|
||||
return fmt.Errorf("round-trip: %w", err)
|
||||
}
|
||||
if err := verifyNotFound(b); err != nil {
|
||||
return fmt.Errorf("not-found: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
调用者编写一个薄测试来接入他们的实现:
|
||||
|
||||
```go
|
||||
func TestMyBackend(t *testing.T) {
|
||||
b := mybackend.New(t)
|
||||
if err := storagetest.Verify(b); err != nil {
|
||||
t.Errorf("MyBackend failed acceptance: %v", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 设计可扩展的验证函数
|
||||
|
||||
**返回错误,而非 `*testing.T` 失败。** 这使验证函数可作为普通 Go 函数使用 — 调用者决定违规是 `t.Error` 还是 `t.Fatal`。
|
||||
|
||||
```go
|
||||
// 好:返回错误 — 调用者控制测试流程
|
||||
func ExercisePlayer(b *chess.Board, p chess.Player) error {
|
||||
move := p.Move()
|
||||
if putsOwnKingIntoCheck(b, move) {
|
||||
return &IllegalMoveError{Move: move, Reason: "puts own king in check"}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 不好:调用 t.Fatal — 调用者失去控制
|
||||
func ExercisePlayer(t *testing.T, b *chess.Board, p chess.Player) {
|
||||
t.Helper()
|
||||
move := p.Move()
|
||||
if putsOwnKingIntoCheck(b, move) {
|
||||
t.Fatalf("illegal move: %v puts own king in check", move)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**在需要丰富诊断信息时使用自定义错误类型**:
|
||||
|
||||
```go
|
||||
type IllegalMoveError struct {
|
||||
Move chess.Move
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (e *IllegalMoveError) Error() string {
|
||||
return fmt.Sprintf("illegal move %v: %s", e.Move, e.Reason)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 何时使用验证 API vs 简单辅助函数
|
||||
|
||||
| 场景 | 使用方式 |
|
||||
|------|----------|
|
||||
| 你拥有的接口,由他人实现 | `*test` 包中的验证 API |
|
||||
| 同一包中跨测试共享设置 | 使用 `t.Helper()` 的测试辅助函数 |
|
||||
| 在 2-3 个测试中重用的复杂断言 | 返回 `error` 或 `bool` 的辅助函数 |
|
||||
| 一次性的设置或比较 | 内联测试代码 |
|
||||
|
||||
**验证 API** 在以下场景值得额外的包:
|
||||
- 多个外部包将实现你的接口
|
||||
- 契约有容易被忽略的非显而易见的不变量
|
||||
- 你想为"正确行为"提供单一事实来源
|
||||
|
||||
**简单辅助函数** 在以下场景更好:
|
||||
- 辅助函数是直接的设置或比较函数
|
||||
- 重用是偶然的,不是已发布契约的一部分
|
||||
|
||||
---
|
||||
|
||||
## 命名约定
|
||||
|
||||
使用表示范围的动词命名函数:`Verify`、`Exercise`、`RunConformance`。接受被测接口作为参数 — 绝不在验证包内部构造实现。
|
||||
|
||||
| 包 | 函数 | 用途 |
|
||||
|----|------|------|
|
||||
| `storagetest` | `Verify` | 验证 `storage.Backend` |
|
||||
| `chesstest` | `ExercisePlayer` | 验证 `chess.Player` |
|
||||
| `cachetest` | `RunConformance` | `cache.Cache` 的完整一致性套件 |
|
||||
Reference in New Issue
Block a user