Practical Go. Amit Saha

Читать онлайн.
Название Practical Go
Автор произведения Amit Saha
Жанр Программы
Серия
Издательство Программы
Год выпуска 0
isbn 9781119773832



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

"errors" "fmt" "io" "os" "strconv" ) type config struct { numTimes int printUsage bool } var usageString = fmt.Sprintf(`Usage: %s <integer> [-h|--help] A greeter application which prints the name you entered <integer> number of times. `, os.Args[0]) func printUsage(w io.Writer) { fmt.Fprintf(w, usageString) } func validateArgs(c config) error { if !(c.numTimes> 0) { return errors.New("Must specify a number greater than 0") } return nil } // TODO – Insert definition of parseArgs() as earlier // TODO – Insert definition of getName() as earlier // TODO – Insert definition of greetUser() as earlier // TODO – Insert definition of runCmd() as earlier func main() { c, err := parseArgs(os.Args[1:]) if err != nil { fmt.Fprintln(os.Stdout, err) printUsage(os.Stdout) os.Exit(1) } err = validateArgs(c) if err != nil { fmt.Fprintln(os.Stdout, err) printUsage(os.Stdout) os.Exit(1) } err = runCmd(os.Stdin, os.Stdout, c) if err != nil { fmt.Fprintln(os.Stdout, err) os.Exit(1) } }

      The main() function first calls the parseArgs() function with the slice of the command-line arguments, starting from the second argument. We get back two values from the function: c, a config object, and err, an error value. If a non- nil error is returned, the following steps are performed:

      1 Print the error.

      2 Print a usage message by calling the printUsage() function, passing in os.Stdout as the writer.

      3 Terminate the program execution with exit code 1 by calling the Exit() function from the os package.

      If the arguments have been parsed correctly, the validateArgs() function is called with the config object, c, that is returned by parseArgs() .

      Finally, if the validateArgs() function returned a nil error value, the runCmd() function is called, passing it a reader, os.Stdin ; a writer, os.Stdout ; and the config object, c .

      Create a new directory, chap1/manual-parse/, and initialize a module inside it:

      $ mkdir -p chap1/manual-parse $ cd chap1/manual-parse $ go mod init github.com/username/manual-parse

      Next, save Listing 1.1 to a file called main.go, and build it:

      $ go build -o application

      Run the command without specifying any arguments. You will see an error and the following usage message:

      $ ./application Invalid number of arguments Usage: ./application <integer> [-h|--help] A greeter application which prints the name you entered <integer> number of times.

      In addition, you will also see that the exit code of the program is 1 .

      $ echo $? 1

      If you are using PowerShell on Windows, you can use echo $LastExitCode to see the exit code.

      This is another notable behavior of command-line applications that you should look to preserve. Any non-successful execution should result in a non-zero exit code upon termination using the Exit() function defined in the os package.

      $ ./application -help Usage: ./application <integer> [-h|-help] A greeter application which prints the name you entered <integer> number of times.

      Finally, let's see what a successful execution of the program looks like:

      $ ./application 5 Your name please? Press the Enter key when done. Joe Cool Nice to meet you Joe Cool Nice to meet you Joe Cool Nice to meet you Joe Cool Nice to meet you Joe Cool Nice to meet you Joe Cool

      You have manually tested that your application behaves as expected under three different input scenarios:

      1 No command-line argument specified.

      2 -h or -help is specified as a command-line argument.

      3 A greeting is displayed to the user a specified number of times.

      Manual testing is error prone and cumbersome, however. Next, you will learn to write automated tests for your application.

      The standard library's testing package contains everything you need to write tests to verify the behavior of your application.

      Let's consider the parseArgs() function first. It is defined as follows:

      func parseArgs(args []string) (config, error) {}

      It has one input: a slice of strings representing the command-line arguments specified to the program during invocation. The return values are a value of type config and a value of type error .

      The testConfig structure will be used to encapsulate a specific test case: a slice of strings representing the input command-line arguments in the args field, expected error value returned in the err field, and the expected config value returned in the embedded config struct field:

      An example test case is

      { args: []string{"-h"}, err: nil, config: config{printUsage: true, numTimes: 0}, },

      This test case verifies the behavior when -h is specified as the command-line argument when executing the application.

      We add a few more test cases and initialize a slice of test cases as follows:

      tests := []testConfig{ { args: []string{"-h"}, err: nil, config: config{printUsage: true, numTimes: 0}, }, { args: []string{"10"}, err: nil, config: config{printUsage: false, numTimes: 10}, }, { args: []string{"abc"}, err: errors.New("strconv.Atoi: parsing \"abc\": invalid syntax"), config: config{printUsage: false, numTimes: 0}, }, { args: []string{"1", "foo"}, err: errors.New("Invalid number of arguments"), config: config{printUsage: false, numTimes: 0}, }, }

      In the same directory as you saved Listing 1.1, save Listing 1.2 into a file called parse_flags_test.go. Now run the test using the go test command:

      $ go test -v === RUN TestParseArgs --- PASS: TestParseArgs (0.00s) PASS ok github.com/practicalgo/code/chap1/manual-parse 0.093

      Passing