测试指南
测试类型
SPIRE 包含多种类型的测试:
单元测试: 测试单个函数和组件
集成测试: 测试组件间交互
端到端测试: 测试完整工作流程
运行测试
所有测试
# 运行所有单元测试
make test
# 带 race 检测
make race-test
特定包
# 测试特定包
go test ./pkg/server/...
# 带详细输出
go test -v ./pkg/server/...
# 测试特定函数
go test -v -run TestFunctionName ./pkg/server/...
集成测试
# 运行集成测试
make integration
# 特定套件
cd test/integration
./test.sh suites/basic
测试覆盖率
生成覆盖率报告
# 运行测试并生成覆盖率
go test -coverprofile=coverage.out ./...
# 查看覆盖率
go tool cover -func=coverage.out
# HTML 报告
go tool cover -html=coverage.out -o coverage.html
包级覆盖率
go test -cover ./pkg/server/...
编写测试
单元测试示例
package mypackage
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMyFunction(t *testing.T) {
// 准备
input := "test"
expected := "TEST"
// 执行
result := MyFunction(input)
// 验证
assert.Equal(t, expected, result)
}
func TestMyFunctionError(t *testing.T) {
// 测试错误情况
_, err := MyFunction("")
require.Error(t, err)
assert.Contains(t, err.Error(), "empty input")
}
表驱动测试
func TestMyFunction(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{
name: "basic",
input: "test",
expected: "TEST",
},
{
name: "empty",
input: "",
wantErr: true,
},
{
name: "with spaces",
input: "hello world",
expected: "HELLO WORLD",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := MyFunction(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.expected, result)
})
}
}
使用 Mocks
//go:generate mockgen -destination=mock_store.go -package=mypackage . Store
type Store interface {
Get(key string) (string, error)
Set(key, value string) error
}
func TestWithMock(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockStore := NewMockStore(ctrl)
// 设置期望
mockStore.EXPECT().
Get("key1").
Return("value1", nil).
Times(1)
// 测试
result, err := myFunctionUsingStore(mockStore, "key1")
require.NoError(t, err)
assert.Equal(t, "value1", result)
}
使用测试夹具
import (
"testing"
"github.com/spiffe/spire/test/spiretest"
)
func TestWithFixtures(t *testing.T) {
// 使用测试 CA
ca := spiretest.NewCA(t)
// 生成 SVID
svid := ca.CreateX509SVID("spiffe://example.org/test")
// 测试...
}
测试工具
test/spiretest
提供 SPIRE 特定的测试工具:
import "github.com/spiffe/spire/test/spiretest"
func TestExample(t *testing.T) {
// 临时目录
dir := spiretest.TempDir(t)
// 测试 gRPC 服务
spiretest.ServeGRPC(t, server, func(conn *grpc.ClientConn) {
// 测试...
})
}
test/fakes
提供 fake 实现:
import "github.com/spiffe/spire/test/fakes/fakeagentstore"
func TestWithFake(t *testing.T) {
store := fakeagentstore.New()
store.SetAgentInfo(&common.AttestedNode{
SpiffeId: "spiffe://example.org/agent",
})
// 测试...
}
test/clock
提供可控的时钟:
import "github.com/spiffe/spire/test/clock"
func TestWithClock(t *testing.T) {
clk := clock.NewMock(t)
// 设置时间
clk.Set(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
// 时间前进
clk.Add(time.Hour)
// 测试...
}
集成测试
结构
test/integration/
├── setup/ # 测试设置脚本
├── suites/ # 测试套件
│ ├── basic/
│ ├── federation/
│ └── ...
└── test.sh # 主测试脚本
运行集成测试
# 所有套件
./test/integration/test.sh
# 特定套件
./test/integration/test.sh suites/basic
# 带调试
DEBUG=1 ./test/integration/test.sh suites/basic
编写集成测试
#!/bin/bash
# test/integration/suites/mytest/test.sh
source "${TESTDIR}/common"
# 启动 Server
start_server server.conf
# 启动 Agent
start_agent agent.conf
# 创建注册条目
create_registration_entry \
-spiffeID spiffe://example.org/test \
-selector unix:uid:1000
# 验证
check_svid "spiffe://example.org/test"
调试测试
详细输出
go test -v -count=1 ./pkg/server/...
特定测试
go test -v -run "TestMyFunction/specific_case" ./pkg/...
跳过缓存
go test -count=1 ./...
超时
go test -timeout 10m ./...
CI/CD 测试
GitHub Actions
项目使用 GitHub Actions 运行测试:
# .github/workflows/pr_build.yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: .go-version
- run: make test
本地模拟 CI
# 运行与 CI 相同的测试
make ci
最佳实践
测试建议
表驱动测试: 使用表驱动测试覆盖多种情况
并行测试: 使用
t.Parallel()加速测试测试隔离: 确保测试不依赖外部状态
有意义的名称: 测试名称应描述测试内容
覆盖边界情况: 测试空值、错误路径等
保持简短: 每个测试聚焦单一行为