このコードを考えると
_func doomed() {
os.Exit(1)
}
_
この関数の呼び出しが_go test
_を使用して存在することを正しくテストするにはどうすればよいですか?これは一連のテスト内で行う必要があります。つまり、os.Exit()
呼び出しは他のテストに影響を与えることができず、トラップする必要があります。
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 でサブプロセステストについて語っています。
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は、この種の作業、およびフォールトインジェクションやその他の困難なタスクに関して、非常に強力です。 いくつかの注意点があります です。
外部から(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)
}
できません。exec.Command
を使用して戻り値をテストする必要があります。
テスト用のコード:
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
}