
Biblioteca de inyección de fallos para Go que añade puntos de fallo controlables en tiempo de ejecución para provocar pánicos, suspensiones, retornos y rutas de error condicionales mediante variables de entorno.
Una implementación de failpoints para Golang. Los failpoints se utilizan para añadir puntos de código donde se pueden inyectar errores de forma controlada por el usuario. Un failpoint es un fragmento de código que solo se ejecuta cuando el failpoint correspondiente está activo.
failpoint-ctl)Compila failpoint-ctl desde el código fuente
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-ctl
Inyecta failpoints en tu programa, por ejemplo:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Transforma tu código con failpoint-ctl enable
Compila con go build
Activa los failpoints con la variable de entorno GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
Nota: GO_FAILPOINTS no funciona con el tipo de marcador InjectCall.
Si usas go run para ejecutar el test, no olvides añadir el archivo generado binding__failpoint_binding__.go a tu comando, así:
GO_FAILPOINTS="main/testPanic=return(true)" go run your-program.go binding__failpoint_binding__.go
failpoint-toolexec)Compila failpoint-toolexec desde el código fuente
git clone https://github.com/pingcap/failpoint.git
cd failpoint
make
ls bin/failpoint-toolexec
Inyecta failpoints en tu programa, por ejemplo:
package main
import "github.com/pingcap/failpoint"
func main() {
failpoint.Inject("testPanic", func() {
panic("failpoint triggerd")
})
}
Usa un caché de compilación separado para evitar mezclar cachés sin failpoint-toolexec, y compila
GOCACHE=/tmp/failpoint-cache go build -toolexec path/to/failpoint-toolexec
Activa los failpoints con la variable de entorno GO_FAILPOINTS
GO_FAILPOINTS="main/testPanic=return(true)" ./your-program
También puedes usar go run o go test, por ejemplo:
GOCACHE=/tmp/failpoint-cache GO_FAILPOINTS="main/testPanic=return(true)" go run -toolexec path/to/failpoint-toolexec your-program.go
Define el failpoint en código Golang válido, no en comentarios ni en nada más
El failpoint no tiene ningún costo adicional
La rutina del failpoint es escribible/legible y debe ser verificada por un compilador
El código generado por la definición del failpoint es fácil de leer
Mantiene los mismos números de línea que los códigos de inyección (más fácil de depurar)
Soporta pruebas paralelas con context.Context
Failpoint
Un failpoint es un fragmento de código que solo se ejecuta cuando el failpoint correspondiente está activo.
El cierre nunca se ejecutará si se ejecuta failpoint.Disable("failpoint-name-for-demo").
var outerVar = "declare in outer scope"
failpoint.Inject("failpoint-name-for-demo", func(val failpoint.Value) {
fmt.Println("unit-test", val, outerVar)
})
Funciones marcadoras
Son solo funciones vacías
Son fáciles de escribir/leer
Introducen una verificación del compilador para los failpoints que no se pueden compilar en el modo normal si el código del failpoint es inválido
Lista de funciones marcadoras
func Inject(fpname string, fpblock func(val Value)) {}func InjectContext(fpname string, ctx context.Context, fpblock func(val Value)) {}func InjectCall(fpname string, args ...any) {}func Break(label ...string) {}func Goto(label string) {}func Continue(label ...string) {}func Fallthrough() {}func Return(results ...interface{}) {}func Label(label string) {}Variables de entorno de failpoint soportadas
Los failpoints se pueden habilitar exportando variables de entorno con el siguiente patrón, que es bastante similar a las VARIABLES SYSCTL de failpoints en FreeBSD
Nota: InjectCall no se puede habilitar mediante variables de entorno.
[<percent>%][<count>*]<type>[(args...)][-><more terms>]
El argumento especifica qué acción tomar; puede ser uno de:
Puedes llamar a failpoint.Inject para inyectar un failpoint en el punto de llamada, donde failpoint-name se
usa para disparar el failpoint y failpoint-closure se expandirá como el cuerpo de la sentencia IF.
failpoint.Inject("failpoint-name", func(val failpoint.Value) {
failpoint.Return("unit-test", val)
})
El código convertido se ve así:
if val, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
return "unit-test", val
}
failpoint.Value es el valor que se pasa mediante failpoint.Enable("failpoint-name", "return(5)"),
el cual se puede ignorar.
failpoint.Inject("failpoint-name", func(_ failpoint.Value) {
fmt.Println("unit-test")
})
O
failpoint.Inject("failpoint-name", func() {
fmt.Println("unit-test")
})
Y el código convertido se ve así:
if _, _err_ := failpoint.Eval(_curpkg_("failpoint-name")); _err_ == nil {
fmt.Println("unit-test")
}
Además, el cierre del failpoint puede ser una función que reciba context.Context. Puedes
hacer cosas personalizadas con context.Context, como controlar si un failpoint está
activo en pruebas paralelas u otros casos. Por ejemplo:
failpoint.InjectContext(ctx, "failpoint-name", func(val failpoint.Value) {
fmt.Println("unit-test", val)
})
El código convertido se ve así: