web-dev-qa-db-ja.com

Goでos.exitシナリオをテストする方法

このコードを考えると

_func doomed() {
  os.Exit(1)
}
_

この関数の呼び出しが_go test_を使用して存在することを正しくテストするにはどうすればよいですか?これは一連のテスト内で行う必要があります。つまり、os.Exit()呼び出しは他のテストに影響を与えることができず、トラップする必要があります。

29
mbrevoort

Andrew Gerrand(Goチームのコアメンバーの1人)による 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スイッチを介して)。また、2番目の呼び出しでチェックされる環境変数(BE_CRASHER=1)を介してフラグを渡し、設定されている場合は、テスト中のシステムを呼び出し、直後に戻って無限ループに陥らないようにします。したがって、元の呼び出しサイトに戻り、実際の終了コードを検証できるようになります。

出典: スライド2 アンドリューのプレゼンテーション。 2番目のスライドには プレゼンテーションのビデオ へのリンクも含まれています。 47:09 でサブプロセステストについて語っています。

37
Timo Reimann

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")
}

monkeyは、この種の作業、およびフォールトインジェクションやその他の困難なタスクに関して、非常に強力です。 いくつかの注意点があります です。

9
Allen Luce

外部から(os.Exitを使用して)テストをシミュレートしないと、実際のexec.Commandをテストできないと思います。

つまり、インターフェイスまたは関数のタイプを作成し、テストでnoop実装を使用することで目標を達成できる可能性があります。

プレイグラウンドに移動

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)
}
7
Matt Self

できません。exec.Commandを使用して戻り値をテストする必要があります。

1
OneOfOne

テスト用のコード:

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
}
0
Alex Geer