鑑於這種代碼如何測試os.exit場景中去
func doomed() {
os.Exit(1)
}
如何正確測試調用這個函數會導致使用go test
的存在嗎?這需要在一套測試中進行,換句話說,呼叫不會影響其他測試,應該被困住。
鑑於這種代碼如何測試os.exit場景中去
func doomed() {
os.Exit(1)
}
如何正確測試調用這個函數會導致使用go test
的存在嗎?這需要在一套測試中進行,換句話說,呼叫不會影響其他測試,應該被困住。
我不認爲你可以在沒有模擬外部測試(使用exec.Command
)的過程中測試實際的os.Exit
。
這就是說,你可能能夠通過來實現自己的目標創建的界面或功能型,然後使用你的測試一個空操作執行:
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)
}
你可以添加測試嗎? – 030 2015-11-16 00:45:07
代碼來進行測試:
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
}
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
談到子測試中,我這樣做是通過使用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。
當然,這不是對問題的直接回答,這就是爲什麼我不把它作爲一個整體寫出來,而是總體上:避免編寫這樣的代碼。如果你只在世界末尾有'Exit'('main'),[像這個模式一樣](http://stackoverflow.com/a/18969976/455009),那麼你不會被這樣寫痛苦的測試作爲(良好)接受的解決方案在這裏。我完全承認你可能一直在測試你不能輕易重構的其他代碼,但只希望這些建議對未來的讀者有所幫助...... – ches 2016-08-07 11:44:13
如果你確實遵循這種模式,並且恰巧使用了Gomega,它[非常酷'gexec' package](http://onsi.github.io/gomega/#gexec-testing-external-processes),這對於以黑匣子方式測試可執行文件的結果非常好。 – ches 2016-08-07 11:46:47