良好的测试习惯能让测试更有效,维护成本更低。
func Test<函数名>_<场景>_<期望结果>(t *testing.T) {}
func TestGetUser_ValidID_ReturnsUser(t *testing.T) {}
func TestGetUser_InvalidID_ReturnsError(t *testing.T) {}
func TestCreateUser_MissingName_ReturnsBadRequest(t *testing.T) {}
func TestUserAPI(t *testing.T) {
gin.SetMode(gin.TestMode)
t.Run("创建用户", func(t *testing.T) {
t.Run("正常创建", func(t *testing.T) {
// 测试代码
})
t.Run("参数验证失败", func(t *testing.T) {
// 测试代码
})
})
t.Run("获取用户", func(t *testing.T) {
t.Run("用户存在", func(t *testing.T) {
// 测试代码
})
t.Run("用户不存在", func(t *testing.T) {
// 测试代码
})
})
}
type TestSuite struct {
Router *gin.Engine
DB *sql.DB
Cleanup func()
}
func SetupTestSuite(t *testing.T) *TestSuite {
gin.SetMode(gin.TestMode)
db := setupTestDB(t)
router := setupRouter(db)
return &TestSuite{
Router: router,
DB: db,
Cleanup: func() {
db.Close()
},
}
}
func TestWithSuite(t *testing.T) {
suite := SetupTestSuite(t)
defer suite.Cleanup()
// 使用 suite.Router 和 suite.DB
}
type RequestBuilder struct {
method string
path string
body io.Reader
headers map[string]string
cookies []*http.Cookie
}
func NewRequest(method, path string) *RequestBuilder {
return &RequestBuilder{
method: method,
path: path,
headers: make(map[string]string),
}
}
func (b *RequestBuilder) WithBody(body string) *RequestBuilder {
b.body = strings.NewReader(body)
return b
}
func (b *RequestBuilder) WithJSON(body interface{}) *RequestBuilder {
data, _ := json.Marshal(body)
b.body = bytes.NewReader(data)
b.headers["Content-Type"] = "application/json"
return b
}
func (b *RequestBuilder) WithHeader(key, value string) *RequestBuilder {
b.headers[key] = value
return b
}
func (b *RequestBuilder) WithAuth(token string) *RequestBuilder {
b.headers["Authorization"] = "Bearer " + token
return b
}
func (b *RequestBuilder) Build() *http.Request {
req, _ := http.NewRequest(b.method, b.path, b.body)
for k, v := range b.headers {
req.Header.Set(k, v)
}
for _, c := range b.cookies {
req.AddCookie(c)
}
return req
}
func TestWithBuilder(t *testing.T) {
r := setupRouter()
req := NewRequest("POST", "/users").
WithJSON(map[string]string{"name": "张三"}).
WithAuth("token").
Build()
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
}
func AssertJSONEquals(t *testing.T, expected, actual string) {
var expectedJSON, actualJSON interface{}
json.Unmarshal([]byte(expected), &expectedJSON)
json.Unmarshal([]byte(actual), &actualJSON)
assert.Equal(t, expectedJSON, actualJSON)
}
func AssertStatusCode(t *testing.T, expected int, recorder *httptest.ResponseRecorder) {
if recorder.Code != expected {
t.Errorf("Expected status %d, got %d. Body: %s",
expected, recorder.Code, recorder.Body.String())
}
}
func AssertError(t *testing.T, body string, expectedError string) {
var response map[string]interface{}
json.Unmarshal([]byte(body), &response)
assert.Contains(t, response["error"], expectedError)
}
type UserFactory struct{}
func (f *UserFactory) Create(overrides ...func(*User)) *User {
user := &User{
ID: 1,
Name: "测试用户",
Email: "test@example.com",
Role: "user",
}
for _, override := range overrides {
override(user)
}
return user
}
func TestUserFactory(t *testing.T) {
factory := &UserFactory{}
admin := factory.Create(func(u *User) {
u.Role = "admin"
})
assert.Equal(t, "admin", admin.Role)
}
type UserRepository interface {
FindByID(id int) (*User, error)
Create(user *User) error
Update(user *User) error
Delete(id int) error
}
type MockUserRepository struct {
FindByIDFunc func(id int) (*User, error)
CreateFunc func(user *User) error
UpdateFunc func(user *User) error
DeleteFunc func(id int) error
}
func (m *MockUserRepository) FindByID(id int) (*User, error) {
return m.FindByIDFunc(id)
}
func (m *MockUserRepository) Create(user *User) error {
return m.CreateFunc(user)
}
func TestWithMock(t *testing.T) {
mock := &MockUserRepository{
FindByIDFunc: func(id int) (*User, error) {
return &User{ID: id, Name: "测试"}, nil
},
}
user, err := mock.FindByID(1)
assert.NoError(t, err)
assert.Equal(t, 1, user.ID)
}
func TestWithCleanup(t *testing.T) {
// 创建测试数据
user := createTestUser(t)
// 注册清理函数
t.Cleanup(func() {
deleteTestUser(t, user.ID)
})
// 测试代码
}
func createTestUser(t *testing.T) *User {
// 创建用户
return &User{ID: 1}
}
func deleteTestUser(t *testing.T, id int) {
// 删除用户
}
良好的测试实践包括:清晰的命名、合理的组织、可复用的辅助函数、独立的数据工厂。测试应该快速、可靠、独立。使用 t.Cleanup 确保资源清理,使用 Mock 隔离外部依赖。测试代码和业务代码一样重要,值得用心维护。