Getting Started
Testing Methods & Approaches
export_test.go
: Used for test data definitions without affecting production code- Setup/Teardown patterns:
func setUp(testName string) func() {
fmt.Printf("\tsetUp fixture for %s\n", testName)
return func() {
fmt.Printf("\ttearDown fixture for %s\n", testName)
}
}
func TestFunc1(t *testing.T) {
defer setUp(t.Name())()
fmt.Printf("\tExecute test: %s\n", t.Name())
}
func TestMain(m *testing.M) {
defer pkgSetUp("package demo_test")()
m.Run()
}
-
Unit Testing
- Core business logic in
go-zero
services (e.g.,logic
layer) - Mock dependencies using:
- sql-mock for databases
- redismock for Redis
- testify-mock for other interfaces
- Alternative mocking solutions: monkey
- Core business logic in
-
Integration Testing
- Handle service dependencies using:
go-mysql-server
for databasemini-redis
for Redis- Or real service instances
- Handle service dependencies using:
Example Repository
- https://github.com/seth-shi/go-zero-testing-example
- Service Architecture:
- id: Snowflake ID service (no dependencies)
- post: Depends on ID service, MySQL, Redis
├─app
│ ├─id
│ └─post
└─pkg (shared utilities)
Unit Testing
ID Service Implementation
// proto definition remains unchanged
Post Service Implementation
// proto definition remains unchanged
Key Components
- Service Context:
type ServiceContext struct {
Config config.Config
Redis *redis.Client
IdRpc id.IdClient
Query *do.Query // GORM generated
}
- Business Logic:
func (l *GetLogic) Get(in *post.PostRequest) (*post.PostResponse, error) {
// Database query
p, err := l.svcCtx.Query.Post.WithContext().Where(...).First()
// Redis increment
val, err := l.svcCtx.Redis.Incr(...).Result()
// Response mapping
return &post.PostResponse{...}, nil
}
Unit Test Implementation
- Test Cases:
func TestGetLogic_Get(t *testing.T) {
// Mock database response
mockVal.DatabaseMock.ExpectQuery(...).WillReturnRows(...)
// Test normal flow
resp, err := logic.Get(&post.PostRequest{Id: 1})
require.NoError(t, err)
// Test Redis failure
mockVal.RedisMock.ExpectIncr(...).SetErr(...)
// Test database failure
mockVal.DatabaseMock.ExpectQuery(...).WillReturnError(...)
}
- Mock Infrastructure:
// Creates mocked database, Redis, and ID service
func GetValue() value {
db, _ := makeDatabase()
redis, _ := redismock.NewClientMock()
return value{
IdServer: &idMock{},
Database: db,
Redis: redis,
}
}
Integration Testing
Service Configuration
- Modified main function:
func main() {
ctx := initializeDependencies()
server := configureGRPCServer(ctx)
server.Start()
}
Integration Test Setup
- Test Entrypoint:
func TestMain(m *testing.M) {
// Initialize fake dependencies
svcCtxGet = func() (*svc.ServiceContext, error) {
return &svc.ServiceContext{
Redis: fakeRedis,
IdRpc: fakeIDService,
Query: fakeDB,
}, nil
}
go main() // Start service
os.Exit(m.Run())
}
- Test Case:
func TestGet(t *testing.T) {
conn := createGRPCClient()
resp, err := client.Get(context.Background(), &post.PostRequest{...})
require.NoError(t, err)
require.Equal(t, expectedID, resp.GetId())
}
Fake Services
- Database & Redis:
func FakerDatabaseServer() string {
// Setup in-memory MySQL
}
func FakerRedisServer() (*miniredis.Miniredis, string) {
// Setup embedded Redis
}
Conclusion
- Complete example repository: https://github.com/seth-shi/go-zero-testing-example
- Key takeaways:
- Clear separation between unit and integration tests
- Comprehensive mocking strategy
- Maintainable test infrastructure