Skip to content

Is this how I should test an app? #1195

Description

@per9000

Is this how I should test an app?

We encountered a problem (duplicated -o flags in one of the subcommands) and didn't realize it until some time later. So, now, we're thinking about writing simple tests that just run the all subcommands to see that they don't crash.

As far as I can tell, this has been covered previously (#731, #1157) -- but recommendations/guidelines doesn't seem to have made it into the documentation, so I thought I'd try to make a minimal example and see if that is close to what you recommend. If it is, then this (or something similar) could perhaps be included in some documentation/wiki?

(I'm not an expert at go, so the modules/packages are a bit dirty and give us lint warnings.)


main.go:

package main

import (
	"fmt"
	"os"

	"github.com/urfave/cli"
)

func hw(ctx *cli.Context) {
	fmt.Println("Hi.")
}

func hello(ctx *cli.Context) {
	fmt.Println("Hello", ctx.String("topic"))
}

func helloworld(ctx *cli.Context) {
	topic := ctx.String("topic")
	offset := ctx.Int64("offset")

	for ; offset > 0; offset-- {
		fmt.Print(" ")
	}
	fmt.Println("Hello", topic)
}

var offsetflg cli.Int64Flag
var topicflg cli.StringFlag

// Public in order to test them
var HwCmd, HelloCmd, HelloWorldCmd cli.Command

func init() {
	offsetflg = cli.Int64Flag{Name: "offset, o", Value: 0, Usage: "Space offset"}
	topicflg = cli.StringFlag{Name: "topic, o", Value: "World", Usage: "Hello topic"}

	HwCmd = cli.Command{
		Name:   "hw",
		Usage:  "Just print hi.",
		Action: hw,
	}

	HelloCmd = cli.Command{
		Name:   "hello",
		Usage:  "Hello world with customizable topic.",
		Action: hello,
		Flags:  []cli.Flag{topicflg},
	}

	HelloWorldCmd = cli.Command{
		Name:   "helloworld",
		Usage:  "Advanced hello with offset and topic.",
		Action: helloworld,
		Flags:  []cli.Flag{topicflg, offsetflg},
	}
}

func main() {
	app := cli.NewApp()
	app.Usage = "Example urfave cli app with flag conflicts."
	app.Commands = cli.Commands{HwCmd, HelloCmd, HelloWorldCmd}

	err := app.Run(os.Args)
	if err != nil {
		fmt.Println("ooops...")
	}
}

main_test.go

package main

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/urfave/cli"
)

func TestHi(t *testing.T) {
	app := cli.NewApp()
	app.Commands = cli.Commands{HwCmd}
	err := app.Run([]string{"appname", "hw"})
	assert.Nil(t, err, "HW should not return an error")
}

func TestHello(t *testing.T) {
	app := cli.NewApp()
	app.Commands = cli.Commands{HelloCmd}
	err := app.Run([]string{"appname", "hello"})
	assert.Nil(t, err, "Hello should not return an error")
}

func TestHelloWorld(t *testing.T) {
	app := cli.NewApp()
	app.Commands = cli.Commands{HelloWorldCmd}
	err := app.Run([]string{"appname", "helloworld"})
	assert.Nil(t, err, "Hello World should not return an error")
}

go.mod

module example.com/mycliapp

go 1.13

require (
	github.com/stretchr/testify v1.6.1
	github.com/urfave/cli v1.22.4
)

Now, if we run the tests it does indeed find the problem:

$ go test ./...
Hi.
Hello World
helloworld flag redefined: o
--- FAIL: TestHelloWorld (0.00s)
panic: helloworld flag redefined: o [recovered]
	panic: helloworld flag redefined: o

goroutine 22 [running]:
testing.tRunner.func1(0xc0000f6500)
	/usr/local/go/src/testing/testing.go:874 +0x3a3
panic(0x7b6080, 0xc000091b60)
[...]

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/v1relates to / is being considered for v1kind/questionsomeone asking a questionstatus/triagemaintainers still need to look into this

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions