How to unit test a Cobra CLI command which requires internal mocks?

2.2k views Asked by At

I have defined the following Cobra subcommand that I'd like to test the output for using testify:

var childCmd = &cobra.Command{
    Args:  cobra.MinimumNArgs(1),
    RunE: func(cmd *cobra.Command, args []string) error {
        id := args[0]
        conn := getConn()
        c := newClient(conn)

        out, err := c.getResult(cmd.Context(), id)
        if err != nil {
            return err
        }

        fmt.Printf(
            "Name:\t%v %v\nCity:\t%v\n",
            out.GetFirstName().String(),
            out.GetLastName().String(),
            out.GetCity().String(),
        )

        return nil
    },
}

func init() {
    rootCmd.AddCommand(childCmd)
}

I can test the actual output with something like this:

func executeCommand(root *cobra.Command, args ...string) (output string, err error) {
    buf := new(bytes.Buffer)
    root.SetOut(buf)
    root.SetErr(buf)
    root.SetArgs(args)

    err = root.Execute()
    if err != nil {
        fmt.Println(err)
    }

    return buf.String(), err
}

func TestGetResult(t *testing.T) {
    rootCmd.AddCommand(childCmd)
    output, _ := executeCommand(rootCmd, "child", "1")
    assert.Equal(t, "test", output)
}

But what I'm missing is how to mock the gRPC client and the its respective call to getResult. Is there a better way to create a new gRPC client than within the RunE func that would help facilitate mocking? As of right now, the unit test attempts to connect to a non-existent gRPC client and fails.

1

There are 1 answers

0
Burak Serdar On

One option is to run the gRPC server within the unit test using a random port and have the client connect to that. This is possible if you have the gRPC server in your tree as well, and if it will not bring in other things you cannot mock.

The easier option is to extract the gRPC initialization to a function variable, and set it to return a mock during the unit test:

// Get connection and return client
func GetGRPCClient() (MyClient,error) {
  ...
}

var getClient = GetGRPCClient

var childCmd = &cobra.Command{
  // use getClient in the command implementation
}


***_test.go:

func getTestGRPCClient() (MyClient,error) {
  // Construct a mock client and return it
}

func TestCommand(t *testing.T) { 
   getClient=getTestGRPCClient
   // Run the test
}