如何测试 Golang 命令行输出?

huangapple go评论111阅读模式
英文:

how do you test golang command line output

问题

我想测试一个使用golang命令行应用程序的输出,但我不太确定如何使用go的测试库来做到这一点。

假设我有一个像这样的程序:

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. )
  6. func main() {
  7. const (
  8. cityDefault = "San Francisco"
  9. cityDoc = "the city you want the forecast for"
  10. )
  11. var city string
  12. flag.StringVar(&city, "city", cityDefault, cityDoc)
  13. flag.StringVar(&city, "c", cityDefault, cityDoc)
  14. flag.Parse()
  15. fmt.Println(city)
  16. }

我想测试这两个命令的输出:

  1. $ ./myapp -c "Los Angeles"
  2. $ ./myapp -city "Los Angeles"

...输出应该是 Lost Angeles。所以,我猜问题是,你如何进行集成测试来测试golang命令行应用程序的输出?

英文:

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:

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. )
  6. func main() {
  7. const (
  8. cityDefault = "San Francisco"
  9. cityDoc = "the city you want the forecast for"
  10. )
  11. var city string
  12. flag.StringVar(&city, "city", cityDefault, cityDoc)
  13. flag.StringVar(&city, "c", cityDefault, cityDoc)
  14. flag.Parse()
  15. fmt.Println(city)
  16. }

I'd like to test that both of these:

  1. $ ./myapp -c "Los Angeles"
  2. $ ./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?

答案1

得分: 4

这是一个解析命令行参数的不好的示例,但它展示了我在应用程序中测试命令行参数时使用的框架。

main.go

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. func main() {
  7. var city string
  8. parseFlags(&city, os.Args)
  9. log.Println(city)
  10. }
  11. func parseFlags(result *string, args []string) {
  12. cityDefault := "San Francisco"
  13. switch len(args) {
  14. case 3:
  15. *result = args[2]
  16. default:
  17. *result = cityDefault
  18. }
  19. }

main_unit_test.go

  1. package main
  2. import (
  3. "log"
  4. "testing"
  5. )
  6. // TestParseFlags - test our command line flags
  7. func TestParseFlags(t *testing.T) {
  8. var parseFlagsTests = []struct {
  9. flags []string // input flags to the command line
  10. expected string // expected
  11. }{
  12. {[]string{"/fake/loc/main"}, "San Francisco"},
  13. {[]string{"/fake/loc/main", "-c", "Los Angeles"}, "Los Angeles"},
  14. {[]string{"/fake/loc/main", "--city", "Los Angeles"}, "Los Angeles"},
  15. }
  16. for _, tt := range parseFlagsTests {
  17. var output string
  18. parseFlags(&output, tt.flags)
  19. if output != tt.expected {
  20. t.Errorf("expected: %s, actual: %s", tt.expected, output)
  21. }
  22. }
  23. }

我通常使用这个包来解析我所有应用程序中的命令行参数。我会按照以下结构编写代码(未显示测试代码,但它们通常遵循上面示例的主要思路):

main.go

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. "myDir/cli"
  6. )
  7. func main() {
  8. // 获取用户输入的命令行标志
  9. cliFlags := cli.FlagsStruct{}
  10. cliErr := cli.StartCLI(&cliFlags, os.Args)
  11. if cliErr != nil {
  12. log.Println("获取命令行参数时出错")
  13. log.Fatal(cliErr)
  14. }
  15. // 做一些事情...
  16. }

/myDir/cli.go

  1. package cli
  2. import "github.com/urfave/cli"
  3. // FlagsStruct - 保存命令行参数
  4. type FlagsStruct struct {
  5. MyFlag string
  6. }
  7. // StartCLI - 收集命令行参数
  8. func StartCLI(cliFlags *FlagsStruct, args []string) error {
  9. app := cli.NewApp()
  10. app.Action = func(ctx *cli.Context) error {
  11. MyFlag := ctx.GlobalString("my-flag")
  12. // 构建要发送回主函数的 cli 结构体
  13. cliFlags.MyFlag = MyFlag
  14. return nil
  15. }
  16. app.Authors = []cli.Author{
  17. {
  18. Email: "my@email.com",
  19. Name: "Adam Hanna",
  20. },
  21. }
  22. app.Flags = []cli.Flag{
  23. cli.StringFlag{
  24. Name: "my-flag, f",
  25. Usage: "这里是我的标志用法",
  26. Value: "myDefault",
  27. },
  28. }
  29. app.Name = "myAppName"
  30. app.Usage = "我的应用程序的用法"
  31. app.Version = "0.0.1"
  32. return app.Run(args)
  33. }
英文:

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

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. func main() {
  7. var city string
  8. parseFlags(&city, os.Args)
  9. log.Println(city)
  10. }
  11. func parseFlags(result *string, args []string) {
  12. cityDefault := "San Francisco"
  13. switch len(args) {
  14. case 3:
  15. *result = args[2]
  16. default:
  17. *result = cityDefault
  18. }
  19. }

main_unit_test.go

  1. package main
  2. import (
  3. "log"
  4. "testing"
  5. )
  6. // TestParseFlags - test our command line flags
  7. func TestParseFlags(t *testing.T) {
  8. var parseFlagsTests = []struct {
  9. flags []string // input flags to the command line
  10. expected string // expected
  11. }{
  12. {[]string{"/fake/loc/main"}, "San Francisco"},
  13. {[]string{"/fake/loc/main", "-c", "Los Angeles"}, "Los Angeles"},
  14. {[]string{"/fake/loc/main", "--city", "Los Angeles"}, "Los Angeles"},
  15. }
  16. for _, tt := range parseFlagsTests {
  17. var output string
  18. parseFlags(&output, tt.flags)
  19. if output != tt.expected {
  20. t.Errorf("expected: %s, actual: %s", tt.expected, output)
  21. }
  22. }
  23. }

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

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. "myDir/cli"
  6. )
  7. func main() {
  8. // Grab the user inputed CLI flags
  9. cliFlags := cli.FlagsStruct{}
  10. cliErr := cli.StartCLI(&cliFlags, os.Args)
  11. if cliErr != nil {
  12. log.Println("Error grabbing command line args")
  13. log.Fatal(cliErr)
  14. }
  15. // Do stuff ...
  16. }

/myDir/cli.go

  1. package cli
  2. import "github.com/urfave/cli"
  3. // FlagsStruct - holds command line args
  4. type FlagsStruct struct {
  5. MyFlag string
  6. }
  7. // StartCLI - gathers command line args
  8. func StartCLI(cliFlags *FlagsStruct, args []string) error {
  9. app := cli.NewApp()
  10. app.Action = func(ctx *cli.Context) error {
  11. MyFlag := ctx.GlobalString("my-flag")
  12. // build the cli struct to send back to main
  13. cliFlags.MyFlag = MyFlag
  14. return nil
  15. }
  16. app.Authors = []cli.Author{
  17. {
  18. Email: "my@email.com",
  19. Name: "Adam Hanna",
  20. },
  21. }
  22. app.Flags = []cli.Flag{
  23. cli.StringFlag{
  24. Name: "my-flag, f",
  25. Usage: "My flag usage goes here",
  26. Value: "myDefault",
  27. },
  28. }
  29. app.Name = "myAppName"
  30. app.Usage = "My App's Usage"
  31. app.Version = "0.0.1"
  32. return app.Run(args)
  33. }

答案2

得分: 2

如何使用 test "$(./myapp -c "洛杉矶")" = "洛杉矶" 进行测试?
对于 -city 参数也是一样。这与 Go 无关,只需让你的集成测试套件执行测试即可。

英文:

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.

huangapple
  • 本文由 发表于 2014年12月4日 01:26:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/27277917.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定