Testing Overview
Kratos services should be tested at multiple levels:- Unit Tests: Test individual functions and business logic
- Integration Tests: Test data layer and external dependencies
- Service Tests: Test service layer and API contracts
- End-to-End Tests: Test complete request flows
Unit Testing
package biz
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// Mock repository
type MockUserRepo struct {
mock.Mock
}
func (m *MockUserRepo) CreateUser(ctx context.Context, user *User) error {
args := m.Called(ctx, user)
return args.Error(0)
}
func (m *MockUserRepo) GetUser(ctx context.Context, id string) (*User, error) {
args := m.Called(ctx, id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*User), args.Error(1)
}
func TestUserUseCase_CreateUser(t *testing.T) {
mockRepo := new(MockUserRepo)
useCase := NewUserUseCase(mockRepo, nil)
tests := []struct {
name string
user *User
mockFn func()
wantErr bool
}{
{
name: "successful creation",
user: &User{
Name: "John Doe",
Email: "[email protected]",
},
mockFn: func() {
mockRepo.On("CreateUser", mock.Anything, mock.AnythingOfType("*biz.User")).Return(nil)
},
wantErr: false,
},
{
name: "duplicate email",
user: &User{
Name: "Jane Doe",
Email: "[email protected]",
},
mockFn: func() {
mockRepo.On("CreateUser", mock.Anything, mock.AnythingOfType("*biz.User")).Return(ErrUserExists)
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockRepo.ExpectedCalls = nil
tt.mockFn()
err := useCase.CreateUser(context.Background(), tt.user)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
mockRepo.AssertExpectations(t)
})
}
}
package biz
import "testing"
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
want bool
}{
{"valid email", "[email protected]", true},
{"missing @", "userexample.com", false},
{"missing domain", "user@", false},
{"empty string", "", false},
{"with subdomain", "[email protected]", true},
{"special characters", "[email protected]", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ValidateEmail(tt.email); got != tt.want {
t.Errorf("ValidateEmail() = %v, want %v", got, tt.want)
}
})
}
}
Integration Testing
Testing Data Layer
Test your data layer with a test database:internal/data/user_test.go
Using Test Containers
Use real databases with testcontainers:Service Testing
Testing Service Layer
Test your service implementation:internal/service/user_test.go
Testing HTTP Handlers
Test HTTP endpoints directly:internal/server/http_test.go
gRPC Testing
Testing gRPC Services
Test gRPC services with bufconn:internal/server/grpc_test.go
End-to-End Testing
Full Integration Tests
Test complete request flows:test/e2e/user_test.go
Test Helpers
Common Test Utilities
test/helper/helper.go
Best Practices
Isolation
Keep tests isolated and independent of each other
Coverage
Aim for high test coverage, especially for business logic
Fast Tests
Keep unit tests fast; use integration tests sparingly
Clear Names
Use descriptive test names that explain what is being tested
Next Steps
Deployment
Deploy your tested service
CI/CD
Set up continuous integration