Ahnii!
Ever struggled with testing Cobra CLI applications? Dependency injection makes it dramatically easier. This post walks through the approach with practical examples.
Why Dependency Injection?
Key benefits for CLI apps:
- Easier to mock dependencies
- More testable code
- Cleaner separation of concerns
- Flexible configuration
Basic Setup
Here's our basic CLI structure with DI:
type AppDependencies struct {
Logger Logger
Config Config
Client HTTPClient
}
func NewRootCmd(deps *AppDependencies) *cobra.Command {
cmd := &cobra.Command{
Use: "mycli",
Short: "My CLI application",
RunE: func(cmd *cobra.Command, args []string) error {
return runRoot(deps, args)
},
}
return cmd
}
Testing Strategy
- Mock Dependencies
type MockLogger struct {
mock.Mock
}
func TestRootCommand(t *testing.T) {
mockLogger := &MockLogger{}
deps := &AppDependencies{
Logger: mockLogger,
}
cmd := NewRootCmd(deps)
assert.NotNil(t, cmd)
}
- Test Command Execution
func TestCommandExecution(t *testing.T) {
deps := setupTestDependencies()
cmd := NewRootCmd(deps)
output, err := executeCommand(cmd, "arg1", "--flag=value")
assert.NoError(t, err)
assert.Contains(t, output, "expected output")
}
Best Practices
- Keep dependencies minimal and focused
- Use interfaces for flexibility
- Test edge cases thoroughly
- Mock external services
Common Patterns
- Configuration Injection
func NewConfig() *Config {
return &Config{
// Default values
}
}
- Logger Injection
type Logger interface {
Info(msg string, args ...interface{})
Error(msg string, args ...interface{})
}
Dependency injection might seem like overhead at first, but it pays off in testability and maintainability. Start small and refactor as needed.
Baamaapii
Top comments (0)