how do you test golang command line output

go, testing

Solution

This is a bad example of parsing command line args, but it shows the framework I use to test command line args in my apps.

main.go

package main

import (
    "log"
    "os"
)

func main() {
    var city string
    parseFlags(&city, os.Args)

    log.Println(city)
}

func parseFlags(result *string, args []string) {
    cityDefault := "San Francisco"

    switch len(args) {
    case 3:
        *result = args[2]
    default:
        *result = cityDefault
    }
}

main_unit_test.go

package main

import (
    "log"
    "testing"
)

// TestParseFlags - test our command line flags
func TestParseFlags(t *testing.T) {
    var parseFlagsTests = []struct {
        flags    []string // input flags to the command line
        expected string   // expected
    }{
        {[]string{"/fake/loc/main"}, "San Francisco"},
        {[]string{"/fake/loc/main", "-c", "Los Angeles"}, "Los Angeles"},
        {[]string{"/fake/loc/main", "--city", "Los Angeles"}, "Los Angeles"},
    }

    for _, tt := range parseFlagsTests {
        var output string
        parseFlags(&output, tt.flags)
        if output != tt.expected {
            t.Errorf("expected: %s, actual: %s", tt.expected, output)
        }
    }
}

I typically use this package to parse command line args in all of my apps. And I'll structure my code as follows (tests not shown, but they generally follow the gist of the test shown above):

main.go

package main

import (
    "log"
    "os"

    "myDir/cli"
)

func main() {
    // Grab the user inputed CLI flags
    cliFlags := cli.FlagsStruct{}
    cliErr := cli.StartCLI(&cliFlags, os.Args)
    if cliErr != nil {
        log.Println("Error grabbing command line args")
        log.Fatal(cliErr)
    }

    // Do stuff ...
}

/myDir/cli.go

package cli

import "github.com/urfave/cli"

// FlagsStruct - holds command line args
type FlagsStruct struct {
    MyFlag string
}

// StartCLI - gathers command line args
func StartCLI(cliFlags *FlagsStruct, args []string) error {
    app := cli.NewApp()
    app.Action = func(ctx *cli.Context) error {
        MyFlag := ctx.GlobalString("my-flag")

        // build the cli struct to send back to main
        cliFlags.MyFlag = MyFlag
        return nil
    }
    app.Authors = []cli.Author{
        {
            Email: "my@email.com",
            Name:  "Adam Hanna",
        },
    }
    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:  "my-flag, f",
            Usage: "My flag usage goes here",
            Value: "myDefault",
        },
    }
    app.Name = "myAppName"
    app.Usage = "My App's Usage"
    app.Version = "0.0.1"
    return app.Run(args)
}

Problem

I'd like to test the output of a golang command line app, but I'm not quite sure how to do that with go's testing library. Let's say I have a program like this: ``` package main import ( "flag" "fmt" ) func main() { const ( cityDefault = "San Francisco" cityDoc = "the city you want the forecast for" ) var city string flag.StringVar(&city, "city", cityDefault, cityDoc) flag.StringVar(&city, "c", cityDefault, cityDoc) flag.Parse() fmt.Println(city) } ``` I'd like to test that both of these: ``` $ ./myapp -c "Los Angeles" $ ./myapp -city "Los Angeles" ``` ... output `Lost Angeles`. So, I guess the question is, how do you go about integration testing the output of a golang command line app?

Original source

Related problems