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?
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)
}
How about test "$(./myapp -c "Los Angeles")" = "Los Angeles"
and the same for -city. This has nothing to do with Go, just let your integration test suite do the test.