Compte tenu de ce code
func doomed() {
os.Exit(1)
}
Comment puis-je tester correctement que l'appel de cette fonction entraînera une existence en utilisant go test
? Cela doit se produire dans une suite de tests, en d'autres termes, l'appel os.Exit()
ne peut pas avoir d'impact sur les autres tests et doit être intercepté.
Il y a présentation par Andrew Gerrand (l'un des membres principaux de l'équipe Go) où il montre comment le faire.
Étant donné une fonction (dans main.go
)
package main
import (
"fmt"
"os"
)
func Crasher() {
fmt.Println("Going down in flames!")
os.Exit(1)
}
voici comment le tester (via 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)
}
Le code invoque go test
à nouveau dans un processus distinct via exec.Command
, limitant l'exécution au test TestCrasher
(via le -test.run=TestCrasher
commutateur). Il passe également dans un drapeau via une variable d'environnement (BE_CRASHER=1
) que la deuxième invocation vérifie et, s'il est défini, appelle le système en cours de test, retournant immédiatement après pour éviter de courir dans une boucle infinie. Ainsi, nous sommes renvoyés sur notre site d'appel d'origine et nous pouvons maintenant valider le code de sortie réel.
Source: diapositive 2 de la présentation d'Andrew. La deuxième diapositive contient également un lien vers la vidéo de présentation . Il parle de tests de sous-processus à 47:09
Je le fais en utilisant 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")
}
singe est super puissant quand il s'agit de ce genre de travail, et pour l'injection de fautes et autres tâches difficiles. Cela vient avec quelques mises en garde .
Je ne pense pas que vous puissiez tester le os.Exit
Sans simuler le test de l'extérieur (en utilisant exec.Command
).
Cela dit, vous pourriez être en mesure d'atteindre votre objectif en créant une interface ou un type de fonction , puis en utilisant une implémentation noop dans vos tests:
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)
}
Vous ne pouvez pas, vous devez utiliser exec.Command
et testez la valeur renvoyée.
Code de test:
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)
}
Code de test:
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
}