如何在Go中测试os.exit场景

时间:2014-10-06 21:56:48

标签: testing go exit-code

鉴于此代码

func doomed() {
  os.Exit(1)
}

如何正确测试调用此函数将导致使用go test存在?这需要在一系列测试中进行,换句话说,os.Exit()调用不会影响其他测试,应该被捕获。

5 个答案:

答案 0 :(得分:35)

Andrew Gerrand(Go团队的核心成员之一)有一个presentation,他在那里展示了如何做到这一点。

给定一个函数(在main.go

package main

import (
    "fmt"
    "os"
)

func Crasher() {
    fmt.Println("Going down in flames!")
    os.Exit(1)
}

这里是你如何测试它(通过main_test.go):

package main

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

func TestCrasher(t *testing.T) {
    if os.Getenv("BE_CRASHER") == "1" {
        Crasher()
        return
    }
    cmd := exec.Command(os.Args[0], "-test.run=TestCrasher")
    cmd.Env = append(os.Environ(), "BE_CRASHER=1")
    err := cmd.Run()
    if e, ok := err.(*exec.ExitError); ok && !e.Success() {
        return
    }
    t.Fatalf("process ran with err %v, want exit status 1", err)
}

代码执行的操作是在go test的单独进程中再次调用exec.Command,将执行限制为TestCrasher测试(通过-test.run=TestCrasher开关)。它还通过第二次调用检查的环境变量(BE_CRASHER=1)传递一个标志,如果设置,则调用被测系统,之后立即返回以防止进入无限循环。因此,我们将被退回到原始呼叫站点,现在可以验证实际的退出代码。

来源:安德鲁的演讲Slide 23。第二张幻灯片还包含指向presentation's video的链接。 他在47:09

讨论了子流程测试

答案 1 :(得分:9)

我使用bouk/monkey

执行此操作
func TestDoomed(t *testing.T) {
  fakeExit := func(int) {
    panic("os.Exit called")      
  }
  patch := monkey.Patch(os.Exit, fakeExit)
  defer patch.Unpatch()
  assert.PanicsWithValue(t, "os.Exit called", doomed, "os.Exit was not called")
}

猴子在这类工作,以及故障注入和其他困难任务方面都非常强大。确实来了with some caveats

答案 2 :(得分:6)

我认为您不能在不模拟外部测试(使用os.Exit)的情况下测试实际的exec.Command

也就是说,您可以通过创建界面或函数类型来实现目标,然后在测试中使用noop实现:

Go Playground

package main

import "os"
import "fmt"

type exiter func (code int)

func main() {
    doExit(func(code int){})
    fmt.Println("got here")
    doExit(func(code int){ os.Exit(code)})
}

func doExit(exit exiter) {
    exit(1)
}

答案 3 :(得分:1)

你不能,你必须使用exec.Command并测试返回的值。

答案 4 :(得分:0)

测试代码:

package main
import "os"

var my_private_exit_function func(code int) = os.Exit

func main() {
    MyAbstractFunctionAndExit(1)
}

func MyAbstractFunctionAndExit(exit int) {
    my_private_exit_function(exit)
}

测试代码:

package main

import (
    "os"
    "testing"
)

func TestMyAbstractFunctionAndExit(t *testing.T) {
    var ok bool = false // The default value can be omitted :)

    // Prepare testing
    my_private_exit_function = func(c int) {
        ok = true
    }
    // Run function
    MyAbstractFunctionAndExit(1)
    // Check
    if ok == false {
        t.Errorf("Error in AbstractFunction()")
    }
    // Restore if need
    my_private_exit_function = os.Exit
}