Practical Go. Amit Saha

Чтение книги онлайн.

Читать онлайн книгу Practical Go - Amit Saha страница 14

Автор:
Жанр:
Серия:
Издательство:
Practical Go - Amit Saha

Скачать книгу

chap1/manual-parse/run_cmd_test.go package main import ( "bytes" "errors" "strings" "testing" ) func TestRunCmd(t *testing.T) { // TODO Insert definition tests[] array as earlier byteBuf := new(bytes.Buffer) for _, tc := range tests { rd := strings.NewReader(tc.input) err := runCmd(rd, byteBuf, tc.c) if err != nil && tc.err == nil { t.Fatalf("Expected nil error, got: %v\n", err) } if tc.err != nil && err.Error() != tc.err.Error() { t.Fatalf("Expected error: %v, Got error: %v\n", tc.err.Error(), err.Error()) } gotMsg := byteBuf.String() if gotMsg != tc.output { t.Errorf("Expected stdout message to be: %v, Got: %v\n", tc.output, gotMsg) } byteBuf.Reset() } }

      We call the byteBuf.Reset() method so that the buffer is emptied before executing the next test case. Save Listing 1.4 into the same directory as Listings 1.1, 1.2, and 1.3. Name the file run_cmd_test.go and run all of the tests:

      You may be curious to find out what the test coverage looks like and visually see which parts of your code are not tested. To do so, run the following command first to create a coverage profile:

      $ go test -coverprofile cover.out PASS coverage: 71.7% of statements ok github.com/practicalgo/code/chap1/manual-parse 0.084s

      The above output tells us that our tests cover 71.7 percent of the code in main.go. To see which parts of the code are covered, run the following:

      $ go tool cover -html=cover.out

      This will open your default browser application and show the coverage of your code in an HTML file. Notably, you will see that the main() function is reported as uncovered since we didn't write a test for it. This leads nicely to Exercise 1.1.

       EXERCISE 1.1: TESTING THE MAIN() FUNCTION In this exercise, you will write a test for the main() function. However, unlike with other functions, you will need to test the exit status for different input arguments. To do so, your test should do the following:

      1 Build the application. You will find using the special TestMain() function useful here.

      2 Execute the application with different command-line arguments using the os.Exec() function. This will allow you to verify both the standard output and the exit code.

      Congratulations! You have written your first command-line application. You parsed the os.Args slice to allow the user to provide input to the application. You learned how to make use of the io . Reader and io . Writer interfaces to write code that is unit testable.

      Next, we will see how the standard library's flag package automatically takes care of the command-line argument parsing, validation of the type of data, and more.

      Before we dive into the flag package, let's refresh our memory of what a typical command-line application's user interface looks like. Let's consider a command-line application called application. Typically, it will have an interface similar to the following:

      The user interface has the following components:

       -h is a Boolean option usually specified to print a help text.

       -n <value> expects the user to specify a value for the option, n. The application's logic determines the expected data type for the value.

       -silent is another Boolean option. Specifying it sets the value to true.

       arg1 and arg2 are referred to as positional arguments. A positional argument’s data type and interpretation is completely determined by the application.

      The flag package implements types and methods to write command-line applications with standard behavior as above. When you specify the -h option while executing the application, all of the other arguments, if specified, will be ignored and a help message will be printed.

      An application will have a mix of required and optional options.

      It is also worth noting here that any positional argument must be specified after you have specified all of the required options. The flag package stops parsing the arguments once it encounters a positional argument, - or -- .

COMMAND-LINE ARGUMENTS FLAG PARSING BEHAVIOR
-h -n 1 hello -h -n 1 Hello -n 1 – Hello Hello -n 1 A help message is displayed.A help message is displayed.Value of the flag n is set to 1 and Hello is available as a positional argument to the application.Value of the flag n is set to 1 and everything else is ignored-n 1 is ignored.

      Let's see an example by rewriting the greeter application so that the number of times the user's name is printed is specified by the option -n. After the rewrite, the user interface will be as follows:

      Comparing the above to Listing 1.1, the key change is in how the parseArgs() function is written:

      func parseArgs(w io.Writer, args []string) (config, error) { c := config{} fs := flag.NewFlagSet("greeter", flag.ContinueOnError) fs.SetOutput(w) fs.IntVar(&c.numTimes, "n", 0, "Number of times to greet") err := fs.Parse(args) if err != nil { return c, err } if fs.NArg() != 0 { return c, errors.New("Positional arguments specified") } return c, nil }

      The function takes two parameters: a variable, w, whose value satisfies the io.Writer interface, and an array of strings representing the arguments to parse. It returns a config object and an error value. To parse the arguments, a new FlagSet object is created as follows:

      fs := flag.NewFlagSet("greeter", flag.ContinueOnError)

      The NewFlagSet() function defined in the flag package is used to create a FlagSet object. Think of it as an abstraction used

Скачать книгу