如何在 Go lang 中为多个单元测试模拟 exec.Command?

abh*_*hda 5 unit-testing go

我刚刚学习了使用exec.Command()ie, mocking 的单元测试函数exec.Command()。我继续添加更多单元案例,但遇到了无法模拟不同场景输出的问题。

这是hello.go我正在尝试测试的示例代码...

package main

import (
    "fmt"
    "os/exec"
)

var execCommand = exec.Command

func printDate() ([]byte, error) {
    cmd := execCommand("date")
    out, err := cmd.CombinedOutput()
    return out, err
}

func main() {
    fmt.Printf("hello, world\n")
    fmt.Println(printDate())
}
Run Code Online (Sandbox Code Playgroud)

下面是测试代码hello_test.go...

package main

import (
    "fmt"
    "os"
    "os/exec"
    "testing"
)

var mockedExitStatus = 1
var mockedDate = "Sun Aug 20"
var expDate = "Sun Aug 20"

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
    }

    // println("Mocked Data:", mockedDate)
    fmt.Fprintf(os.Stdout, mockedDate)
    os.Exit(mockedExitStatus)
}

func TestPrintDate(t *testing.T) {
    execCommand = fakeExecCommand
    defer func() { execCommand = exec.Command }()

    out, err := printDate()
    print("Std out: ", string(out))
    if err != nil {
        t.Errorf("Expected nil error, got %#v", err)
    }
    if string(out) != expDate {
        t.Errorf("Expected %q, got %q", expDate, string(out))
    }
}

func TestPrintDateUnableToRunError(t *testing.T) {
    execCommand = fakeExecCommand
    defer func() { execCommand = exec.Command }()

    mockedExitStatus = 1
    mockedDate = "Unable to run date command"
    expDate = "Unable to run date command"

    out, err := printDate()
    print("Std out: ", string(out))
    if err != nil {
        t.Errorf("Expected nil error, got %#v", err)
    }
    if string(out) != expDate {
        t.Errorf("Expected %q, got %q", expDate, string(out))
    }
}
Run Code Online (Sandbox Code Playgroud)

go test第二次测试失败TestPrintDateUnableToRunError...

$ go test hello
Std out: Sun Aug 20Std out: Sun Aug 20--- FAIL: TestPrintDateTomorrow (0.01s)
    hello_test.go:62: Expected "Unable to run date command", got "Sun Aug 20"
FAIL
FAIL    hello   0.017s
Run Code Online (Sandbox Code Playgroud)

即使我试图mockedDate在测试用例中设置全局值,它仍然会获得初始化时使用的全局值。是否未设置全局值?或者对该全局变量的更改没有更新TestHelperProcess

abh*_*hda 7

我得到了这个问题的解决方案...

全局值没有设置吗?或者对该全局变量的更改没有在 TestHelperProcess 中更新?

由于 in TestPrintDate(),fakeExecCommand被调用而不是 exec.Command ,并且调用fakeExecCommandrunninggo test来运行 only TestHelperProcess(),所以它完全是一个新的调用,其中 onlyTestHelperProcess()将被执行。由于仅TestHelperProcess()调用了,因此未设置全局变量。

解决方案是在 中设置 Env fakeExecCommand,然后在 中检索它TestHelperProcess()并返回这些值。

PS>TestHelperProcess被重命名为TestExecCommandHelper,并且很少有变量被重命名。

package main

import (
    "fmt"
    "os"
    "os/exec"
    "strconv"
    "testing"
)

var mockedExitStatus = 0
var mockedStdout string

func fakeExecCommand(command string, args ...string) *exec.Cmd {
    cs := []string{"-test.run=TestExecCommandHelper", "--", command}
    cs = append(cs, args...)
    cmd := exec.Command(os.Args[0], cs...)
    es := strconv.Itoa(mockedExitStatus)
    cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1",
        "STDOUT=" + mockedStdout,
        "EXIT_STATUS=" + es}
    return cmd
}

func TestExecCommandHelper(t *testing.T) {
    if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
        return
    }

    // println("Mocked stdout:", os.Getenv("STDOUT"))
    fmt.Fprintf(os.Stdout, os.Getenv("STDOUT"))
    i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS"))
    os.Exit(i)
}

func TestPrintDate(t *testing.T) {
    mockedExitStatus = 1
    mockedStdout = "Sun Aug 201"
    execCommand = fakeExecCommand
    defer func() { execCommand = exec.Command }()
    expDate := "Sun Aug 20"

    out, _ := printDate()
    if string(out) != expDate {
        t.Errorf("Expected %q, got %q", expDate, string(out))
    }
}

func TestPrintDateUnableToRunError(t *testing.T) {
    mockedExitStatus = 1
    mockedStdout = "Unable to run date command"
    execCommand = fakeExecCommand
    defer func() { execCommand = exec.Command }()

    expDate := "Unable to run date command"

    out, _ := printDate()
    // println("Stdout: ", string(out))
    if string(out) != expDate {
        t.Errorf("Expected %q, got %q", expDate, string(out))
    }
}
Run Code Online (Sandbox Code Playgroud)

go test结果如下... (故意失败一项测试以表明模拟工作正常)。

 go test hello
--- FAIL: TestPrintDate (0.01s)
        hello_test.go:45: Expected "Sun Aug 20", got "Sun Aug 201"
FAIL
FAIL    hello   0.018s
Run Code Online (Sandbox Code Playgroud)