2014-10-06 74 views
19

鑑於這種代碼如何測試os.exit場景中去

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

如何正確測試調用這個函數會導致使用go test的存在嗎?這需要在一套測試中進行,換句話說,呼叫不會影響其他測試,應該被困住。

+0

當然,這不是對問題的直接回答,這就是爲什麼我不把它作爲一個整體寫出來,而是總體上:避免編寫這樣的代碼。如果你只在世界末尾有'Exit'('main'),[像這個模式一樣](http://stackoverflow.com/a/18969976/455009),那麼你不會被這樣寫痛苦的測試作爲(良好)接受的解決方案在這裏。我完全承認你可能一直在測試你不能輕易重構的其他代碼,但只希望這些建議對未來的讀者有所幫助...... – ches 2016-08-07 11:44:13

+0

如果你確實遵循這種模式,並且恰巧使用了Gomega,它[非常酷'gexec' package](http://onsi.github.io/gomega/#gexec-testing-external-processes),這對於以黑匣子方式測試可執行文件的結果非常好。 – ches 2016-08-07 11:46:47

回答

1

你不能,你將不得不使用exec.Command並測試返回的值。

+0

請問您可以添加代碼和測試示例嗎? – 030 2015-11-16 00:38:30

6

我不認爲你可以在沒有模擬外部測試(使用exec.Command)的過程中測試實際的os.Exit

這就是說,你可能能夠通過來實現自己的目標創建的界面或功能型,然後使用你的測試一個空操作執行:

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) 
} 
+1

你可以添加測試嗎? – 030 2015-11-16 00:45:07

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 
} 
25

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

+0

運行測試結果:'進程運行err exec:「cmd」:在$ PATH中找不到可執行文件,希望退出狀態1' – 030 2015-11-16 00:29:00

+0

@Alfred您是否將實現和測試保存在單獨的文件中,例如'main。 go'和'main_test.go',分別?我修改了我的答案並仔細檢查了它是否適用於我的機器。 – 2015-11-17 01:50:36

+0

是的。他們是分開的。有可能是某些環境變量'go env'有問題嗎?我總是使用'go test'進行測試,並且我還創建了幾個測試來測試其他文件。 – 030 2015-11-17 10:17:44

4

談到子測試中,我這樣做是通過使用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