普通网友 2019-09-24 16:58
浏览 175

使用Go测试基于urfave / cli的应用程序

I'm writing a small CLI application in Golang using urfave/cli framework and I'd like to write tests for it, but I can't find any useful information on how to test CLI applications, specifically written with the urfave/cli library. I have a lot of flags in the application and some of them are mutually exclusive and I'd like a proper test to stay on top of them - does anyone have an idea how to do it the right way?

EDIT: Consider the following minimal example of application with several flags and restrictions around them. How would you test these flags usage (requirements, exclusivity, etc.) and how they influence the functions when they're set or not?

package main

import (
    "errors"
    "fmt"
    "os"

    "github.com/urfave/cli"
)

func doSomething(flag1 string, flag2 string, flag3 bool, flag4 bool) error {
    err := errors.New("something")
    return err
}

func main() {
    app := cli.NewApp()
    app.Name = "greet"
    app.Usage = "fight the loneliness!"

    var flag1, flag2 string
    var flag3, flag4 bool

    app.Flags = []cli.Flag{
        cli.StringFlag{
            Name:        "flag1",
            Value:       "",
            Usage:       "flag1",
            Destination: &flag1,
        },
        cli.StringFlag{
            Name:        "flag2",
            Value:       "",
            Usage:       "flag2",
            Destination: &flag2,
        },
        cli.BoolFlag{
            Name:        "flag3",
            Usage:       "flag3",
            Destination: &flag3,
        },
        cli.BoolFlag{
            Name:        "flag4",
            Usage:       "flag4",
            Destination: &flag4,
        },
    }

    app.Action = func(c *cli.Context) error {

        if flag1 != "" && c.NumFlags() > 1 {
            fmt.Println("--flag1 flag cannot be used with any other flags")
            cli.ShowAppHelp(c)
            os.Exit(1)
        }

        if flag1 == "" && flag2 == "" || c.NumFlags() < 1 {
            fmt.Println("--flag2 is required")
            cli.ShowAppHelp(c)
            os.Exit(1)
        }

        if flag3 && flag4 {
            fmt.Println("--flag3 and --flag4 flags are mutually exclusive")
            cli.ShowAppHelp(c)
            os.Exit(1)
        }

        err := doSomething(flag1, flag2, flag3, flag4)
        return err
    }

}
  • 写回答

1条回答 默认 最新

  • doufei9946 2019-09-24 23:35
    关注

    As Adrian correctly wrote

    the same way you test anything else

    Given a slightly modified example of the sample code of the project

    package main
    
    import (
      "fmt"
      "log"
      "os"
    
      "github.com/urfave/cli"
    )
    
    func Friend(c *cli.Context) error {
      fmt.Println("Hello friend!")
      return nil
    }
    
    func main() {
      app := cli.NewApp()
      app.Name = "greet"
      app.Usage = "fight the loneliness!"
      app.Action = Friend
    
      err := app.Run(os.Args)
      if err != nil {
        log.Fatal(err)
      }
    }
    

    Since this code actually prints something instead of returning a value you can evaluate, you could use a testable example

    func ExampleFriend(){
       // Yeah, technically, we can save the error check with the code above
       // but this illustrates how you can make sure the output
       // is not what the testable example expects.
       if err := Friend(nil){
         fmt.Printf("Friend: %s",err)
       }
    
      // Output:
      // Hello friend!
    }
    

    Note that Action expects an ActionFunc. Where you define that ActionFunc is pretty much your thing. It could even come from a different package. So it is your design on how good your application will be testable.

    Edit The signature of the value Action expects will change in the future, at least according to the docs. I already find it questionable to use interface{} to be able to pass nil to Action, and then check and type assert for ActionFunc, where a no-op ActionFunc would actually serve the same purpose, but removing an error return value really makes me scratch my head. I strongly recommend to have a look at alecthomas/kingpin for smaller to medium size applications or spf13/cobra, which is suitable even for the most complex of cli applications.

    评论

报告相同问题?

悬赏问题

  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?
  • ¥15 matlab(相关搜索:紧聚焦)
  • ¥15 基于51单片机的厨房煤气泄露检测报警系统设计
  • ¥15 路易威登官网 里边的参数逆向
  • ¥15 Arduino无法同时连接多个hx711模块,如何解决?
  • ¥50 需求一个up主付费课程
  • ¥20 模型在y分布之外的数据上预测能力不好如何解决
  • ¥15 processing提取音乐节奏
  • ¥15 gg加速器加速游戏时,提示不是x86架构