# 测试指南 本项目的测试使用 **Convey 框架**,分为三个层次:单元测试、集成测试和端到端测试。 ## 测试结构 ``` tests/ ├── setup_test.go # 测试设置和通用工具 ├── unit/ # 单元测试 │ ├── config_test.go # 配置测试 │ └── ... # 其他单元测试 ├── integration/ # 集成测试 │ ├── database_test.go # 数据库集成测试 │ └── ... # 其他集成测试 └── e2e/ # 端到端测试 ├── api_test.go # API 测试 └── ... # 其他 E2E 测试 ``` ## Convey 框架概述 Convey 是一个 BDD 风格的 Go 测试框架,提供直观的语法和丰富的断言。 ### 核心概念 - **Convey**: 定义测试上下文,类似于 `Describe` 或 `Context` - **So**: 断言函数,验证预期结果 - **Reset**: 清理函数,在每个测试后执行 ### 基本语法 ```go Convey("测试场景描述", t, func() { Convey("当某个条件发生时", func() { // 准备测试数据 result := SomeFunction() Convey("那么应该得到预期结果", func() { So(result, ShouldEqual, "expected") }) }) Reset(func() { // 清理测试数据 }) }) ``` ## 运行测试 ### 运行所有测试 ```bash go test ./tests/... -v ``` ### 运行特定类型的测试 ```bash # 单元测试 go test ./tests/unit/... -v # 集成测试 go test ./tests/integration/... -v # 端到端测试 go test ./tests/e2e/... -v ``` ### 运行带覆盖率报告的测试 ```bash go test ./tests/... -v -coverprofile=coverage.out go tool cover -html=coverage.out -o coverage.html ``` ### 运行基准测试 ```bash go test ./tests/... -bench=. -v ``` ## 测试环境配置 ### 单元测试 - 不需要外部依赖 - 使用内存数据库或模拟对象 - 快速执行 ### 集成测试 - 需要数据库连接 - 使用测试数据库 `v2_test` - 需要启动 Redis 等服务 ### 端到端测试 - 需要完整的应用环境 - 测试真实的 HTTP 请求 - 可能需要 Docker 环境 ## Convey 测试最佳实践 ### 1. 测试结构设计 - 使用描述性的中文场景描述 - 遵循 `当...那么...` 的语义结构 - 嵌套 Convey 块来组织复杂测试逻辑 ```go Convey("用户认证测试", t, func() { var user *User var token string Convey("当用户注册时", func() { user = &User{Name: "测试用户", Email: "test@example.com"} err := user.Register() So(err, ShouldBeNil) Convey("那么用户应该被创建", func() { So(user.ID, ShouldBeGreaterThan, 0) }) }) Convey("当用户登录时", func() { token, err := user.Login("password") So(err, ShouldBeNil) So(token, ShouldNotBeEmpty) Convey("那么应该获得有效的访问令牌", func() { So(len(token), ShouldBeGreaterThan, 0) }) }) }) ``` ### 2. 断言使用 - 使用丰富的 So 断言函数 - 提供有意义的错误消息 - 验证所有重要的方面 ### 3. 数据管理 - 使用 `Reset` 函数进行清理 - 每个测试独立准备数据 - 确保测试间不相互影响 ### 4. 异步测试 - 使用适当的超时设置 - 处理并发测试 - 使用 channel 进行同步 ### 5. 错误处理 - 测试错误情况 - 验证错误消息 - 确保错误处理逻辑正确 ## 常用 Convey 断言 ### 相等性断言 ```go So(value, ShouldEqual, expected) So(value, ShouldNotEqual, expected) So(value, ShouldResemble, expected) // 深度比较 So(value, ShouldNotResemble, expected) ``` ### 类型断言 ```go So(value, ShouldBeNil) So(value, ShouldNotBeNil) So(value, ShouldBeTrue) So(value, ShouldBeFalse) So(value, ShouldBeZeroValue) ``` ### 数值断言 ```go So(value, ShouldBeGreaterThan, expected) So(value, ShouldBeLessThan, expected) So(value, ShouldBeBetween, lower, upper) ``` ### 集合断言 ```go So(slice, ShouldHaveLength, expected) So(slice, ShouldContain, expected) So(slice, ShouldNotContain, expected) So(map, ShouldContainKey, key) ``` ### 字符串断言 ```go So(str, ShouldContainSubstring, substr) So(str, ShouldStartWith, prefix) So(str, ShouldEndWith, suffix) So(str, ShouldMatch, regexp) ``` ### 错误断言 ```go So(err, ShouldBeNil) So(err, ShouldNotBeNil) So(err, ShouldError, expectedError) ``` ## 测试工具 - `goconvey/convey` - BDD 测试框架 - `gomock` - Mock 生成器 - `httptest` - HTTP 测试 - `sqlmock` - 数据库 mock - `testify` - 辅助测试工具(可选) ## 测试示例 ### 配置测试示例 ```go Convey("配置加载测试", t, func() { var config *Config Convey("当从文件加载配置时", func() { config, err := LoadConfig("config.toml") So(err, ShouldBeNil) So(config, ShouldNotBeNil) Convey("那么配置应该正确加载", func() { So(config.App.Mode, ShouldEqual, "development") So(config.Http.Port, ShouldEqual, 8080) }) }) }) ``` ### 数据库测试示例 ```go Convey("数据库操作测试", t, func() { var db *gorm.DB Convey("当连接数据库时", func() { db = SetupTestDB() So(db, ShouldNotBeNil) Convey("那么应该能够创建记录", func() { user := User{Name: "测试用户", Email: "test@example.com"} result := db.Create(&user) So(result.Error, ShouldBeNil) So(user.ID, ShouldBeGreaterThan, 0) }) }) Reset(func() { if db != nil { CleanupTestDB(db) } }) }) ``` ### API 测试示例 ```go Convey("API 端点测试", t, func() { var server *httptest.Server Convey("当启动测试服务器时", func() { server = httptest.NewServer(NewApp()) So(server, ShouldNotBeNil) Convey("那么健康检查端点应该正常工作", func() { resp, err := http.Get(server.URL + "/health") So(err, ShouldBeNil) So(resp.StatusCode, ShouldEqual, http.StatusOK) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) So(result["status"], ShouldEqual, "ok") }) }) Reset(func() { if server != nil { server.Close() } }) }) ``` ## CI/CD 集成 测试会在以下情况下自动运行: - 代码提交时 - 创建 Pull Request 时 - 合并到主分支时 测试结果会影响代码合并决策。Convey 的详细输出有助于快速定位问题。