我刚刚学习了使用exec.Command()
的单元测试函数,即模拟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())
}
以下是测试代码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))
}
}
第二次测试 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
即使我尝试在测试用例中设置全局mockedDate
值,它仍然会获得初始化的全局值。 全球价值未设定?或者,TestHelperProcess
答案 0 :(得分:2)
我得到了解决方案......
全球价值未设定吗?或者,对于那个全局变量的更改没有在TestHelperProcess中更新?
由于在TestPrintDate()
中调用了fakeExecCommand
而不是exec.Command,并且调用fakeExecCommand
只运行go test
TestHelperProcess()
,所以它总是一个新的只调用TestHelperProcess()
的调用。由于仅调用TestHelperProcess()
,因此未设置全局变量。
解决方案是在fakeExecCommand
中设置Env,并在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))
}
}
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
答案 1 :(得分:0)
根据您发布的代码,mockedDate
变量不会执行任何操作。测试和printDate()
的调用都没有使用它,因此TestPrintDateUnableToRunError()
测试的执行方式就像之前的测试一样。
如果要向printDate()
函数添加功能以返回“无法运行日期命令”字符串(在这种情况下),那么第62行的条件将通过。也就是说,当printDate()
的返回值出错时,此类检查应该是不必要的。如果返回的错误是非nil,则返回的输出字符串应该是无效的(或为空,""
)。
我不知道你真的希望printDate()
失败,但就目前情况而言,它无法返回TestPrintDateUnableToRunError()
中您期望的值。