go-unit-tests
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGo Unit Tests
Go单元测试
Generate comprehensive Go unit tests following testify patterns and the Arrange-Act-Assert methodology.
按照testify模式和Arrange-Act-Assert方法论生成全面的Go单元测试。
Planning Phase
规划阶段
Before writing tests, identify:
- Test Structure: Determine if test suite (for structs with dependencies) or individual test functions (for standalone functions) should be used
- Dependencies: Identify dependencies or side effects requiring mocks or stubs
- Test Cases: Define scenarios covering happy paths, edge cases, and error conditions
- Naming: Number each test case clearly (e.g., ,
TestFunction_ValidInput_ReturnsExpectedResult)TestFunction_EmptyInput_ReturnsError
Show the code without explanations during planning.
编写测试前,需明确:
- 测试结构:确定应使用测试套件(适用于带有依赖的结构体)还是独立测试函数(适用于独立函数)
- 依赖项:识别需要mocks或stubs的依赖项或副作用
- 测试用例:定义涵盖正常路径、边缘情况和错误场景的测试场景
- 命名规范:为每个测试用例清晰命名(例如:、
TestFunction_ValidInput_ReturnsExpectedResult)TestFunction_EmptyInput_ReturnsError
规划阶段仅展示代码,不附带解释。
Implementation Patterns
实现模式
Pattern 1: Test Suites for Structs with Dependencies
模式1:带依赖结构体的测试套件
Use from testify for structs with dependencies.
suite.SuiteKey Rules:
- Create suite struct with (System Under Test) field
sut - Implement method to initialize sut and dependencies
SetupTest - Use constructor (typically ) to create instances
NewTypeName - Always use suffix for package name
_test - Use methods for assertions (e.g.,
suite)suite.Equal(v, 10) - Use for error assertions (e.g.,
suite.Require(),suite.Require().ErrorIs)suite.Require().Error - Never use
.AssertExpectations(s.T())
Example:
go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/suite"
)
type MyStructTestSuite struct {
suite.Suite
sut *mypackage.MyStruct
}
func (s *MyStructTestSuite) SetupTest() {
// Initialize sut and dependencies
s.sut = mypackage.New()
}
func TestMyStructSuite(t *testing.T) {
suite.Run(t, new(MyStructTestSuite))
}
func (s *MyStructTestSuite) TestSomeMethod() {
// Arrange
input := "test input"
expected := "expected output"
// Act
result := s.sut.SomeMethod(input)
// Assert
s.Equal(expected, result)
}
func (s *MyStructTestSuite) TestSomeMethod_WithError() {
// Arrange
invalidInput := ""
// Act
result, err := s.sut.SomeMethodWithError(invalidInput)
// Assert
s.Require().Error(err)
s.Empty(result)
}With Mocks:
go
package mypackage_test
import (
"context"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"github.com/example/project/test/mocks"
)
type UserServiceTestSuite struct {
suite.Suite
sut *mypackage.UserService
userRepoMock *mocks.MockUserRepository
tokenServiceMock *mocks.MockTokenService
}
func (s *UserServiceTestSuite) SetupTest() {
// Initialize mocks
s.userRepoMock = mocks.NewMockUserRepository(s.T())
s.tokenServiceMock = mocks.NewMockTokenService(s.T())
// Initialize sut with mocked dependencies
s.sut = mypackage.NewUserService(
s.userRepoMock,
s.tokenServiceMock,
)
}
func TestUserServiceSuite(t *testing.T) {
suite.Run(t, new(UserServiceTestSuite))
}
func (s *UserServiceTestSuite) TestCreateUser_ValidInput_CreatesUser() {
// Arrange
ctx := context.Background()
user := &mypackage.User{
Email: "test@example.com",
Name: "Test User",
}
s.userRepoMock.On("Create", mock.Anything, user).Return(nil)
// Act
err := s.sut.CreateUser(ctx, user)
// Assert
s.Require().NoError(err)
}
func (s *UserServiceTestSuite) TestCreateUser_RepositoryError_ReturnsError() {
// Arrange
ctx := context.Background()
user := &mypackage.User{
Email: "test@example.com",
Name: "Test User",
}
expectedError := errors.New("repository error")
s.userRepoMock.On("Create", mock.Anything, user).Return(expectedError)
// Act
err := s.sut.CreateUser(ctx, user)
// Assert
s.Require().ErrorIs(err, expectedError)
}
func (s *UserServiceTestSuite) TestGenerateToken_ValidUser_ReturnsToken() {
// Arrange
ctx := context.Background()
userID := "user-123"
expectedToken := "token-abc"
s.tokenServiceMock.On(
"Generate",
mock.Anything,
userID,
).Return(expectedToken, nil)
// Act
token, err := s.sut.GenerateToken(ctx, userID)
// Assert
s.Require().NoError(err)
s.Equal(expectedToken, token)
}Mock Rules:
- Always pass for context parameters
mock.Anything - Mock naming follows pattern (e.g.,
MockType,MockUserRepository)MockTokenService - Import mocks with aliases:
user_repository_mocks "github.com/project/internal/domain/repository/mocks"
使用testify的为带有依赖的结构体编写测试。
suite.Suite核心规则:
- 创建包含(被测系统)字段的套件结构体
sut - 实现方法以初始化sut和依赖项
SetupTest - 使用构造函数(通常为)创建实例
NewTypeName - 包名必须以为后缀
_test - 使用方法进行断言(例如:
suite)suite.Equal(v, 10) - 使用进行错误断言(例如:
suite.Require()、suite.Require().ErrorIs)suite.Require().Error - 禁止使用
.AssertExpectations(s.T())
示例:
go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/suite"
)
type MyStructTestSuite struct {
suite.Suite
sut *mypackage.MyStruct
}
func (s *MyStructTestSuite) SetupTest() {
// Initialize sut and dependencies
s.sut = mypackage.New()
}
func TestMyStructSuite(t *testing.T) {
suite.Run(t, new(MyStructTestSuite))
}
func (s *MyStructTestSuite) TestSomeMethod() {
// Arrange
input := "test input"
expected := "expected output"
// Act
result := s.sut.SomeMethod(input)
// Assert
s.Equal(expected, result)
}
func (s *MyStructTestSuite) TestSomeMethod_WithError() {
// Arrange
invalidInput := ""
// Act
result, err := s.sut.SomeMethodWithError(invalidInput)
// Assert
s.Require().Error(err)
s.Empty(result)
}含Mocks的示例:
go
package mypackage_test
import (
"context"
"testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"github.com/example/project/test/mocks"
)
type UserServiceTestSuite struct {
suite.Suite
sut *mypackage.UserService
userRepoMock *mocks.MockUserRepository
tokenServiceMock *mocks.MockTokenService
}
func (s *UserServiceTestSuite) SetupTest() {
// Initialize mocks
s.userRepoMock = mocks.NewMockUserRepository(s.T())
s.tokenServiceMock = mocks.NewMockTokenService(s.T())
// Initialize sut with mocked dependencies
s.sut = mypackage.NewUserService(
s.userRepoMock,
s.tokenServiceMock,
)
}
func TestUserServiceSuite(t *testing.T) {
suite.Run(t, new(UserServiceTestSuite))
}
func (s *UserServiceTestSuite) TestCreateUser_ValidInput_CreatesUser() {
// Arrange
ctx := context.Background()
user := &mypackage.User{
Email: "test@example.com",
Name: "Test User",
}
s.userRepoMock.On("Create", mock.Anything, user).Return(nil)
// Act
err := s.sut.CreateUser(ctx, user)
// Assert
s.Require().NoError(err)
}
func (s *UserServiceTestSuite) TestCreateUser_RepositoryError_ReturnsError() {
// Arrange
ctx := context.Background()
user := &mypackage.User{
Email: "test@example.com",
Name: "Test User",
}
expectedError := errors.New("repository error")
s.userRepoMock.On("Create", mock.Anything, user).Return(expectedError)
// Act
err := s.sut.CreateUser(ctx, user)
// Assert
s.Require().ErrorIs(err, expectedError)
}
func (s *UserServiceTestSuite) TestGenerateToken_ValidUser_ReturnsToken() {
// Arrange
ctx := context.Background()
userID := "user-123"
expectedToken := "token-abc"
s.tokenServiceMock.On(
"Generate",
mock.Anything,
userID,
).Return(expectedToken, nil)
// Act
token, err := s.sut.GenerateToken(ctx, userID)
// Assert
s.Require().NoError(err)
s.Equal(expectedToken, token)
}Mock规则:
- 上下文参数始终传递
mock.Anything - Mock命名遵循格式(例如:
MockType、MockUserRepository)MockTokenService - 使用别名导入mocks:
user_repository_mocks "github.com/project/internal/domain/repository/mocks"
Pattern 2: Tests for Standalone Functions
模式2:独立函数的测试
Use individual test functions with subtests for functions without instances.
Key Rules:
- Create test functions using
func TestXxx(t *testing.T) - Use for subtests covering different scenarios
t.Run - Use for error assertions (e.g.,
require,require.ErrorIs)require.Error
Example:
go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSomeFunction(t *testing.T) {
t.Run("valid input returns expected result", func(t *testing.T) {
// Arrange
input := "test input"
expected := "expected output"
// Act
result := SomeFunction(input)
// Assert
require.Equal(t, expected, result)
})
t.Run("empty input returns error", func(t *testing.T) {
// Arrange
input := ""
// Act
result, err := SomeFunctionWithError(input)
// Assert
require.Error(t, err)
require.Empty(t, result)
})
t.Run("nil input returns error", func(t *testing.T) {
// Arrange
var input *string
// Act
result, err := SomeFunctionWithPointer(input)
// Assert
require.ErrorIs(t, err, ErrNilInput)
require.Empty(t, result)
})
}为无实例的函数创建独立测试函数并使用子测试。
核心规则:
- 使用创建测试函数
func TestXxx(t *testing.T) - 使用创建涵盖不同场景的子测试
t.Run - 使用进行错误断言(例如:
require、require.ErrorIs)require.Error
示例:
go
package mypackage_test
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestSomeFunction(t *testing.T) {
t.Run("valid input returns expected result", func(t *testing.T) {
// Arrange
input := "test input"
expected := "expected output"
// Act
result := SomeFunction(input)
// Assert
require.Equal(t, expected, result)
})
t.Run("empty input returns error", func(t *testing.T) {
// Arrange
input := ""
// Act
result, err := SomeFunctionWithError(input)
// Assert
require.Error(t, err)
require.Empty(t, result)
})
t.Run("nil input returns error", func(t *testing.T) {
// Arrange
var input *string
// Act
result, err := SomeFunctionWithPointer(input)
// Assert
require.ErrorIs(t, err, ErrNilInput)
require.Empty(t, result)
})
}Test Structure Requirements
测试结构要求
Arrange-Act-Assert Pattern
Arrange-Act-Assert模式
Every test must follow AAA pattern with explicit comments:
go
// Arrange
// Act
// Assert每个测试必须遵循AAA模式,并添加明确注释:
go
// Arrange
// Act
// AssertCode Style
代码风格
- Never use inline struct construction; always create variable first
- Maximum 120 characters per line
- Test names must clearly indicate what is being tested
- Add comments for complex test setups or assertions
- 禁止使用内联结构体构造,必须先创建变量
- 每行最多120个字符
- 测试名称必须清晰表明测试内容
- 复杂测试设置或断言需添加注释
Test Coverage
测试覆盖率
- Include happy path scenarios
- Include edge cases
- Include error handling
- Aim for minimum test scenarios possible while maintaining at least 80% coverage
- 包含正常路径场景
- 包含边缘情况
- 包含错误处理场景
- 在保持至少80%覆盖率的前提下,使用最少的测试场景
Completion
完成标识
When tests are complete, respond with: Tests Done, Oh Yeah!
测试编写完成后,回复:Tests Done, Oh Yeah!