Как протестировать сценарии os.exit в Go
Учитывая этот код
func doomed() {
os.Exit(1)
}
Как правильно проверить, что вызов этой функции приведет к существованию с помощью go test
? Это должно происходить в рамках набора тестов, другими словами вызов os.Exit()
не может влиять на другие тесты и должен быть захвачен.
Ответы
Ответ 1
Там презентация Эндрю Джерранда (одного из основных членов команды Go), где он показывает, как это сделать.
Для функции (в 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
), которую второй вызов проверяет и, если задан, вызывает системный тест, возвращающийся сразу после этого, чтобы предотвратить запуск в бесконечный цикл. Таким образом, мы возвращаемся обратно в наш исходный сайт вызова и теперь можем подтвердить действительный код выхода.
Источник: Слайд 23 презентации Эндрю. Второй слайд содержит ссылку на видеоролик .
Он рассказывает о тестах подпроцесса на 47:09
Ответ 2
Я не думаю, что вы можете протестировать фактический 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)
}
Ответ 3
Я делаю это с помощью 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")
}
Обезьяна является сверхмощной, когда дело доходит до такого рода работы, а также для впрыска и других сложных задач. Он приходит с некоторыми оговорками.
Ответ 4
Вы не можете, вам нужно будет использовать exec.Command
и проверить возвращаемое значение.
Ответ 5
Код для тестирования:
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
}