DEV Community

Russell Jones
Russell Jones

Posted on • Originally published at jonesrussell.github.io

Testing Cobra CLI Apps in Go: A DI Approach

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
}
Enter fullscreen mode Exit fullscreen mode

Testing Strategy

  1. 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)
}
Enter fullscreen mode Exit fullscreen mode
  1. 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")
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  • Keep dependencies minimal and focused
  • Use interfaces for flexibility
  • Test edge cases thoroughly
  • Mock external services

Common Patterns

  1. Configuration Injection
func NewConfig() *Config {
    return &Config{
        // Default values
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Logger Injection
type Logger interface {
    Info(msg string, args ...interface{})
    Error(msg string, args ...interface{})
}
Enter fullscreen mode Exit fullscreen mode

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)