Files
2025-09-14 17:58:17 -05:00

220 lines
4.4 KiB
Plaintext

---
description:
globs: pkg/**/*_test.go,pkg/**/*.go
alwaysApply: false
---
# Go Testing Guidelines
## Test Structure
1. Use table-driven tests:
```golang
func TestCalculate(t *testing.T) {
tests := []struct {
name string
input int
want int
wantErr bool
}{
{
name: "positive number",
input: 5,
want: 10,
wantErr: false,
},
{
name: "negative number",
input: -5,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Calculate(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("Calculate() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("Calculate() = %v, want %v", got, tt.want)
}
})
}
}
```
2. Use subtests for better organization:
```golang
func TestService(t *testing.T) {
t.Run("Create", func(t *testing.T) {
// Test creation
})
t.Run("Update", func(t *testing.T) {
// Test update
})
t.Run("Delete", func(t *testing.T) {
// Test deletion
})
}
```
## Test Helpers
1. Create test utilities:
```golang
// Good
func setupTestDB(t *testing.T) (*sql.DB, func()) {
t.Helper() // Mark as helper function
db, err := sql.Open("postgres", "test-connection-string")
if err != nil {
t.Fatalf("setting up test db: %v", err)
}
return db, func() {
db.Close()
}
}
// Usage
func TestDatabase(t *testing.T) {
db, cleanup := setupTestDB(t)
defer cleanup()
// Run tests
}
```
## Mocking
1. Use interfaces for testability:
```golang
// Good
type UserRepository interface {
GetUser(id int) (*User, error)
}
// Mock implementation
type mockUserRepo struct {
users map[int]*User
}
func (m *mockUserRepo) GetUser(id int) (*User, error) {
user, ok := m.users[id]
if !ok {
return nil, ErrNotFound
}
return user, nil
}
```
2. For smaller mocks, less than 200 lines use the testify mock library: github.com/stretchr/testify/mock
See [node_test.go](mdc:pkg/utils/node_test.go) for examples of how to use this
3. For large mocks, more than 200 lines, use github.com/matryer/moq to generate MOQ files
```golang
//go:generate moq -out linux_networking_moq.go . LinuxNetworking
type LinuxNetworking interface {
ipvsCalls
netlinkCalls
}
```
## Test Coverage
1. Run tests with coverage:
```bash
go test -cover ./...
```
2. Generate coverage report:
```bash
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```
## Best Practices
1. Test files should be in the same package:
```golang
// user.go
package user
// user_test.go
package user_test // Use _test suffix for external test package
```
2. Use parallel tests when possible:
```golang
func TestParallel(t *testing.T) {
tests := []struct{
name string
// test cases
}{
// test cases here
}
for _, tt := range tests {
tt := tt // Capture range variable
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // Mark test as parallel
// Test implementation
})
}
}
```
3. Use test fixtures for large (more than 100 lines) amounts of test data rather than in-lining tests:
```golang
// testdata/users.json
// Place test data in testdata directory
```
4. Test race conditions:
```bash
go test -race ./...
```
5. Use the Testify library for assertions: github.com/stretchr/testify/assert
```golang
if testcase.err != nil {
assert.EqualError(t, err, testcase.err.Error())
}
```
6. Fail fast for conditions that would stop a test from executing
```golang
t.Run(testcase.name, func(t *testing.T) {
clientset := fake.NewSimpleClientset()
_, err := clientset.CoreV1().Nodes().Create(context.Background(), testcase.existingNode, metav1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create existing nodes for test: %v", err)
}
}
```
## Integration Tests
1. Use build tags:
```golang
// +build integration
package integration
func TestIntegration(t *testing.T) {
// Integration test implementation
}
```
2. Separate integration test configuration:
```golang
// test_config.go
package config
var TestConfig = Config{
DBHost: "localhost",
DBPort: 5432,
}
```