Golang中测试辅助函数的最佳存放位置探讨
Golang中测试辅助函数的最佳存放位置探讨 我有多个测试都在使用相同的函数,主要是模拟和桩函数功能。这些东西应该放在哪里?有没有惯用的位置来存放这些函数?
谢谢!
我想我已经大致理解了。
假设我有两个测试文件:
main_test.go
user_test.go
这两个文件都在使用一个仅用于测试目的且被多个测试需要的函数。
假设是一个用于创建某种模拟用户的函数。
我应该把它放在哪里?
// 测试相关的辅助函数可以放在单独的测试工具文件中
嗨 mendlock,你应该为测试函数创建一个特定名称的文件。例如,如果你有一个名为"newDeck"的函数,你应该在另一个文件中创建"TestNewDeck"函数。
我还附上了一个示例
你也可以运行"Go test"来执行这些测试并生成结果报告。
致意 
你可以拥有多个 _test.go 文件。如果在文件夹内运行 “go test” 命令,它会找到所有测试。例如我有三个文件 main.go、main_test.go 和 mock_test.go,然后运行 go test -v(-v 表示详细模式),它会找到 main_test.go 中的测试和 mock_test.go 中的结构体,并输出如下内容:
$ go test -v
=== RUN TestUsername
--- PASS: TestUsername (0.00s)
PASS
ok _/Users/xxx/projects/zzz 0.008s
mock_test.go
package main
type MockUser struct {
}
func (u MockUser) Name() string {
return "Peter"
}
main_test.go
package main
import "testing"
func TestUsername(t *testing.T) {
u := MockUser{}
name := u.Name()
if name != "Peter" {
t.Errorf("wanted Peter got %s", name)
}
}
如果你想要的话,可以在更多 _test.go 文件中使用 Mockuser。
在Go项目中,测试辅助函数的最佳存放位置通常遵循以下惯例:
1. 在同包内的测试文件中
最常见的做法是将共享的测试辅助函数放在同包的 *_test.go 文件中:
// user_service.go
package user
type UserService struct {
repo UserRepository
}
func (s *UserService) GetUser(id int) (*User, error) {
return s.repo.FindByID(id)
}
// user_service_test.go
package user
import (
"testing"
"github.com/stretchr/testify/assert"
)
// 测试辅助函数 - 模拟用户
func mockUser() *User {
return &User{
ID: 1,
Name: "测试用户",
Email: "test@example.com",
}
}
// 桩函数 - 模拟仓库
type mockRepository struct{}
func (m *mockRepository) FindByID(id int) (*User, error) {
return mockUser(), nil
}
func TestUserService_GetUser(t *testing.T) {
service := &UserService{repo: &mockRepository{}}
user, err := service.GetUser(1)
assert.NoError(t, err)
assert.Equal(t, "测试用户", user.Name)
}
2. 使用 internal/testutils 包
对于跨多个包共享的测试辅助函数,可以创建 internal/testutils 包:
project/
├── internal/
│ └── testutils/
│ ├── helpers.go
│ └── mocks.go
├── user/
│ ├── user_service.go
│ └── user_service_test.go
└── order/
├── order_service.go
└── order_service_test.go
// internal/testutils/helpers.go
package testutils
import "time"
// 跨包共享的测试辅助函数
func CreateTestTime() time.Time {
return time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
}
func Pointer[T any](v T) *T {
return &v
}
3. 使用测试辅助文件约定
创建专门的测试辅助文件,通常命名为 helpers_test.go 或 test_helpers.go:
// helpers_test.go
package user
import "testing"
var (
TestUserID = 1
TestUserName = "测试用户"
)
// 设置测试环境
func setupTest(t *testing.T) (*UserService, func()) {
repo := &mockRepository{}
service := &UserService{repo: repo}
// 返回清理函数
cleanup := func() {
// 清理资源
}
return service, cleanup
}
// 验证用户数据的辅助函数
func verifyUser(t *testing.T, user *User, expectedID int, expectedName string) {
if user.ID != expectedID {
t.Errorf("期望用户ID %d, 得到 %d", expectedID, user.ID)
}
if user.Name != expectedName {
t.Errorf("期望用户名 %s, 得到 %s", expectedName, user.Name)
}
}
4. 表格驱动测试中的辅助函数
对于表格驱动测试,可以将测试用例定义放在辅助函数中:
// user_service_test.go
package user
import "testing"
type getUserTestCase struct {
name string
userID int
expectedUser *User
expectedError error
setupMock func(repo *mockRepository)
}
func getTestCases() []getUserTestCase {
return []getUserTestCase{
{
name: "成功获取用户",
userID: 1,
expectedUser: &User{
ID: 1,
Name: "用户1",
Email: "user1@example.com",
},
setupMock: func(repo *mockRepository) {
// 设置模拟行为
},
},
{
name: "用户不存在",
userID: 999,
expectedUser: nil,
expectedError: ErrUserNotFound,
setupMock: func(repo *mockRepository) {
// 设置返回错误的模拟行为
},
},
}
}
func TestUserService_GetUser_TableDriven(t *testing.T) {
for _, tc := range getTestCases() {
t.Run(tc.name, func(t *testing.T) {
repo := &mockRepository{}
tc.setupMock(repo)
service := &UserService{repo: repo}
user, err := service.GetUser(tc.userID)
// 验证结果
})
}
}
这些方法遵循Go社区的惯例,保持代码组织清晰且易于维护。选择哪种方式取决于辅助函数的共享范围和项目的具体结构需求。

