Test func main

Testing the edges of a system reveals which paths can actually occur. If you can't test it, can it really happen?

This example shows how to design func main so it can be fully tested with go test. Move logic into func run and handle fatal errors in func end.

func main() {
	verbose := flag.Bool("verbose", false, "")
	flag.Usage = usage(os.Stderr, os.Args[0])
	flag.Parse()

	err := run(os.Stdout, *verbose)

	end(err, os.Stderr, os.Exit)
}

func usage(w io.Writer, name string) func() {
	return func() {
		tpl := `%s: [OPTIONS]
		
Options
`
		fmt.Fprintf(w, tpl, name)
		flag.CommandLine.SetOutput(w)
		flag.PrintDefaults()
	}
}

// end writes out error and calls exit if error is not nil.
func end(err error, stderr io.Writer, exit func(int)) {
	if err != nil {
		fmt.Fprintln(stderr, err)
		exit(1)
	}
}

// ---- put func run in run.go ----

func run(stdout io.Writer, verbose bool) error {
	if verbose {
		fmt.Fprint(stdout, "hello world")
	}
	return nil
}

func Test_main(t *testing.T) {
	os.Args = []string{"", "-verbose"}
	main()
}

func Test_usage(t *testing.T) {
	var buf bytes.Buffer
	usage(&buf, "")()

	got := buf.String()
	if !strings.Contains(got, "-verbose") {
		t.Error(got)
	}
}

func Test_end(t *testing.T) {
	var code *int
	catchExitCode := func(v int) { code = &v }

	end(io.EOF, io.Discard, catchExitCode)

	if code == nil {
		t.Fatal("func exit not called")
	}
	if *code != 1 {
		t.Errorf("%v: not 1", code)
	}
}