首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何模拟*exec.Cmd / exec.Command()?

如何模拟*exec.Cmd / exec.Command()?
EN

Stack Overflow用户
提问于 2022-02-13 15:50:40
回答 4查看 799关注 0票数 0

我需要嘲笑exec.Command()

我可以通过以下方式来模仿它:

代码语言:javascript
复制
var rName string
var rArgs []string

mockExecCommand := func(name string, arg ...string) *exec.Cmd {
    rName = name
    rArgs = arg

    return nil
}

但是,由于返回的exec.Cmd调用Run(),所以在实际的代码中这是行不通的,因为它抱怨零指针。

我试图嘲弄它,就像:

代码语言:javascript
复制
type mock exec.Cmd

func (m *mock) Run() error {
    return nil
}

var rName string
var rArgs []string

mockExecCommand := func(name string, arg ...string) *exec.Cmd {
    rName = name
    rArgs = arg

    m := mock{}

    return &m
}

但它抱怨道:cannot use &m (value of type *mock) as *exec.Cmd value in return statementcompilerIncompatibleAssign

有什么办法可以解决这个问题吗?有更好的方法来模仿exec.Command()吗?

如果我返回一个“模拟”命令,模拟函数就能工作,尽管我也希望控制Run()函数:

代码语言:javascript
复制
var rName string
var rArgs []string

mockExecCommand := func(name string, arg ...string) *exec.Cmd {
    rName = name
    rArgs = arg

    return exec.Command("echo")
}
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2022-02-17 22:22:42

其实有办法做到这一点。所有的功劳都归于的文章。看看下面是怎么回事的解释:

代码语言:javascript
复制
func fakeExecCommand(command string, args...string) *exec.Cmd {
    cs := []string{"-test.run=TestHelperProcess", "--", command}
    cs = append(cs, args...)
    cmd := exec.Command(os.Args[0], cs...)
    cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
    return cmd
}

func TestHelperProcess(t *testing.T){
    if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
        return
    }
    os.Exit(0)
}
票数 0
EN

Stack Overflow用户

发布于 2022-09-28 00:28:43

当劫持测试可执行文件以运行特定函数时,只使用常规依赖注入就更简单了。不需要魔法。

设计一个可以运行命令的接口(例如CommandExecutor),然后将其中一个接口作为运行命令所需的任何函数的输入。然后,您可以在测试期间提供一个满足接口的模拟实现(手工构建,或者使用您选择的工具(如GoMock)生成)。为您的生产代码提供真正的实现(它调用exec包)。您的模拟实现甚至可以对参数进行断言,以便您知道该命令正在被“正确执行”。

票数 1
EN

Stack Overflow用户

发布于 2022-12-03 22:07:56

我所知道的最佳方法是使用多态性。你走在正确的轨道上。在我创建的https://github.com/schollii/go-test-mock-exec-command上有一个详细的解释,因为当我搜索如何模拟os/exec时,我所能找到的只是另一个答案中提到的env变量技术。这种方法是绝对没有必要的,正如我链接到的git的自述自述的那样,它所需要的只是一点多态。

摘要基本上如下:

  1. exec.Cmd创建一个接口类,该类只有应用程序(或模块)代码要使用的必要方法
  2. 创建一个实现该接口的结构(如它只需提到exec.Cmd )
  3. 创建包级var (导出),指向从步骤2返回结构的函数。
  4. 让应用程序代码使用包级var。
  5. 使您的测试创建一个实现该接口的新结构,但只包含输出和退出代码,并使测试将包级var替换为该新结构的实例。

在应用程序代码中,它将如下所示:

代码语言:javascript
复制
type IShellCommand interface {
    Run() error
}

type execShellCommand struct {
    *exec.Cmd
}

func newExecShellCommander(name string, arg ...string) IShellCommand {
    execCmd := exec.Command(name, arg...)
    return execShellCommand{Cmd: execCmd}
}

// override this in tests to mock the git shell command
var shellCommander = newExecShellCommander

func myFuncThatUsesExecCmd() {
    cmd := shellCommander("git", "rev-parse", "--abbrev-ref", "HEAD")
    err := cmd.Run()
    if err != nil {
        // handle error
    } else {
        // process & handle output
    }
}

在测试方面,它看起来如下所示:

代码语言:javascript
复制
type myShellCommand struct {
    RunnerFunc func() error
}

func (sc myShellCommand) Run() error {
    return sc.RunnerFunc()
}

func Test_myFuncThatUsesExecCmd(t *testing.T) {
    // temporarily swap the shell commander
    curShellCommander := shellCommander
    defer func() { shellCommander = curShellCommander }()

    shellCommander = func(name string, arg ...string) IShellCommand {
        fmt.Printf("exec.Command() for %v called with %v and %v\n", t.Name(), name, arg)
        return myShellCommand{
            RunnerFunc: func() error {
                return nil
            },
        }
    }

    // now that shellCommander is mocked, call the function that we want to test:
    myFuncThatUsesExecCmd()
    // do checks
  }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71102318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档