Tests basés sur les propriétés en Go : casser le code plus vite que vous ne l'écrivez

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Quand les tests unitaires mentent et que les bugs persistent



Vous avez écrit 20 tests unitaires. Tous verts. Le CI passe. Vous êtes satisfait. Et puis — un bug en production. Ça vous dit quelque chose ? Scénario classique : vous n'avez testé que les cas que vous avez imaginés vous-même. Mais la réalité, comme par hasard, vous réserve des combinaisons auxquelles vous n'avez même pas pensé.



En Go, il existe une approche puissante mais sous-estimée — le test basé sur les propriétés (ou test de propriétés). Au lieu de vérifier des entrées et sorties spécifiques, vous décrivez les propriétés que votre fonction doit satisfaire, et le framework génère lui-même des centaines de données de test aléatoires. Si une propriété est violée — vous avez trouvé un bug.



Dans cet article, nous allons voir comment mettre en place le test basé sur les propriétés en Go avec la bibliothèque gopter, écrire des exemples concrets et observer quels bugs on peut attraper. C'est parti !



Qu'est-ce que le test basé sur les propriétés et pourquoi en a-t-on besoin ?



Imaginez que vous testez une fonction de tri. Un test unitaire classique :



func TestSort(t *testing.T) {    input := []int{3, 1, 2}    result := sort(input)    expected := []int{1, 2, 3}    if !reflect.DeepEqual(result, expected) {        t.Errorf("got %v, want %v", result, expected)    }}


OK, ça marche. Mais que faire si le tableau est vide ? Et si tous les éléments sont identiques ? Et s'il y a des nombres négatifs ? Vous pouvez écrire 10 tests de plus, mais vous en oublierez toujours.



Le test basé sur les propriétés résout ce problème de manière radicale : vous formulez des invariants — des propriétés qui doivent être vraies pour toute entrée valide. Par exemple, pour un tri :



  • La longueur du tableau trié est égale à la longueur du tableau d'origine.
  • Chaque élément du tableau d'origine est présent dans le tableau trié (et vice versa).
  • Le tableau est trié par ordre croissant.


Le framework (par exemple, gopter ou le package intégré testing/quick) génère des données d'entrée aléatoires et vérifie que toutes les propriétés sont respectées. S'il y a un bug — vous le saurez, même sans savoir où chercher.



Bibliothèques Go pour le test basé sur les propriétés



Dans l'écosystème Go, il existe plusieurs outils :



  • testing/quick — package intégré, minimaliste mais avec des capacités limitées.
  • gopter — bibliothèque puissante avec des générateurs, du shrinking (minimisation des données d'entrée) et des réglages flexibles. Nous la recommandons.
  • rapid — alternative moderne, plus performante.


Nous utiliserons gopter, car elle a la meilleure documentation et communauté. Installation :



go get github.com/leanovate/gopter


Écrire notre premier test basé sur les propriétés en Go



Supposons que nous ayons une fonction Reverse qui inverse une chaîne :



func Reverse(s string) string {    runes := []rune(s)    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {        runes[i], runes[j] = runes[j], runes[i]    }    return string(runes)}


Quelle propriété peut-on vérifier ? Si on inverse une chaîne deux fois, on doit obtenir la chaîne d'origine :



import (    "testing"    "github.com/leanovate/gopter"    "github.com/leanovate/gopter/gen")

func TestReverseTwice(t *testing.T) { properties := gopter.NewProperties(nil)

properties.Property("reverse(reverse(s)) == s", prop.ForAll( func(s string) bool { return Reverse(Reverse(s)) == s }, gen.AnyString(), ))

properties.TestingRun(t)}


Lancez le test — et gopter générera 100 chaînes aléatoires, vérifiera la propriété. Si tout va bien — le test est vert. Sinon — vous obtiendrez un contre-exemple (la plus petite chaîne sur laquelle le test a échoué).



Bo

Blogs

Book Recommendations