Go fake for os.exec

Golang os.exec

我们进行Linux后端服务编程时,经常会进行系统操作。Golang对系统命令执行的支持是   通过os.exec库实现的。  
那么在测试时,我们如何在UT当中,对调用os.exec的地方进行单元测试呢?
这个库的mock并不好处理,在设置exec.Command之后执行它的run方法,所以即使想对它   进行打桩也找不到可以替换的接口和mock对象。

这个包自己的解决办法

在exec这个包的测试文件exec_test.go中,可以看到有这样一段代码: ```Go func helperCommand(t *testing.T, s ...string) *exec.Cmd {
cs := []string{"-test.run=TestHelperProcess", "--"}
cs = append(cs, s...)
cmd := exec.Command(os.Args[0], cs...)
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
return cmd } ``` 上面这个函数在测试用例执行时执行的,并且在函数中执行一个命令为os.Args[0]的系统命令。   os.Args[0]就是这个Go代码在执行go test的时候的执行程序。 通过上面这个代码,go test执行到这个函数时,就会以```"-test.run=TestHelperProcess -- "```   为参数执行go test执行程序。go test的可执行文件,执行机制是执行 test.run指定的测试用例。   那么现在就相当与指定执行```TestHelperProcess```这个用例了。   详细解释见:[测试 os/exec.Command](https://npf.io/2015/06/testing-exec-command/)

应用实例

  1. 第一步,需要把产品代码中对os.exec的调用做个封装:var execCommand = exec.Command
    然后代码中使用的是execCommand,这样做是便于我们下面进行Fake
  2. 测试Fake的写法如下,可以和实际的测试用例不在同一个包中:
    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
     }
    
  3. 最终执行对象是一个测试用例,需要跟被测测试用例在一个包下面:
    func TestHelperProcess(t *testing.T){
         if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
             return
         }
         cmdName = os.Args[3] // 0--是 go test 执行程序 1--是 TestHelperProcess 2-- 是“--”
         fmt.Fprintf(os.Stdout, "command name is %s\n ")
         os.Exit(0)
     }
    
  4. 在用例中进行替换使用就可以了
    execCommand = fakeExecCommand
    defer func(){ execCommand = exec.Command }()
    

这个方法相对优雅,而且可以进行针对不同命令进扩展,以及统计等。

参考文档

  1. 测试 os/exec.Command
  2. Unit Testing Exec Command in Golang


blog comments powered by Disqus
—  原创作品许可 — 署名-非商业性使用-禁止演绎 3.0 未本地化版本 — CC BY-NC-ND 3.0   —