Automatiser votre débugage d’historique Git avec PHPUnit et Behat

Une histoire classique: Pris par la fièvre de votre projet, vous alignez les commits, les uns après les autres, jusqu’au moment fatidique où vous vous rendez compte que vous avez cassé vos tests. Mais depuis combien de commit? Difficile à dire car le bug est insidieux.

Si on jette un œil à l’historique de la branche, on se retrouve avec quelque-chose comme ça:

$ git log
| a1bf1b1 - (HEAD, master) refactor a little bit more (me 54 seconds ago)
| 2d32ee0 - make some stuff (me 7 minutes ago)
| 5aedc45 - refactor my function (me 8 minutes ago)
| de872cb - add a nice thing (me 12 minutes ago)
| c4254ad - modify something (me 12 minutes ago)
| da54d5a - do some refactoring (me 15 minutes ago)
| e57bf5d - (origin/master) ...

On remarque que le HEAD est buggé, tandis que origin fonctionnait. Le bug a été introduit entre temps par l’un de ces commits, mais on ne sait pas lequel…

La première idée qui vient en tête est de faire un checkout sur chacune des révisions et de retester. Mais cela deviendrait vite fastidieux à mesure que s’allonge l’historique.

Pas de panique: git-bisect arrive à la rescousse! Git va rentabiliser le parcours de l’historique grâce à une recherche dichotomique.

On comprend alors tout l’intérêt d’atomiser ses commits. Plus précis ils seront, plus finement Git déterminera les modifications incriminées.

git-bisect manuel

Avant toute chose, il faut initier le bisect:

$ git bisect start

Ensuite, vous devez définir le range de l’historique à parcourir. Dans notre cas, le bug est quelque-part entre HEAD et origin:

$ git bisect bad                        # la révision courante (HEAD) est buggée
$ git bisect good origin/master         # origin était stable

C’est alors que Git lance le bisect et va effectuer un checkout au milieu du range. Vous devez donc lancer vos tests (avec PHPUnit ou Behat, peu importe):

$ bin/phpunit

Si vos tests échouent, vous ne vous posez aucune question:

$ git bisect bad

Même chose si vos tests passent:

$ git bisect good

Git effectue alors un nouveau checkout, on relance les tests, et ainsi de suite.

A chaque étape, Git évalue également le nombre d’étapes restantes:

Bisecting: 2 revisions left to test after this (roughly 2 steps)

Une fois le commit fautif découvert, on stoppe le bisect:

$ git bisect reset

git-bisect automatisé

On a assurément réduit le nombre de commits à devoir tester, mais il faut toujours boucler sur un certain nombre de commits, lancer les tests à chaque fois, et communiquer leurs résultats à Git…

Tant qu’à faire, autant que tout ceci soit automatisé. Ce qui est possible grâce à la sous-commande run. Une fois que vous avez défini le range de l’historique à parcourir, plutôt que de simplement tester manuellement, vous lancez les tests avec cette sous-commande:

$ git bisect run bin/phpunit

A chaque checkout, Git va lancer automatiquement vos tests pour vous. Vous n’avez plus qu’à patienter le temps du bisect, puis à le reseter à la toute fin.

Et tant qu’à faire simple, pourquoi pas se faire un petit alias:

# ~/.gitconfig

[alias]
    debug = !sh -c 'git bisect start && git bisect good $2 && git bisect bad HEAD && git bisect run $1 && git bisect reset' -

que vous pouvez utiliser comme ceci:

$ git debug bin/phpunit origin/master    #git debug <test_command> <good_commit>

Attention aux caches

Attention toutefois à vos caches, en particulier si vous testez avec Behat. Il vous sera certainement nécessaire de vider votre cache applicatif avant chaque lancement de Behat. Le plus simple est donc de surcharger la commande originale de Behat (bin/behat) et de la versionner (.gitignore).

Par exemple, si vous travaillez sous Symfony:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/env bash

# récupération du directory du fichier
BASEDIR=$(dirname $0)

# suppression du cache de symfony
$BASEDIR/../app/console cache:clear -e test

# lancement de behat
$BASEDIR/../vendor/behat/behat/bin/behat $*

# retour du code d'erreur de behat
exit $?

Veillez bien à retourner le code d’erreur de Behat, car c’est lui que Git va interpréter pour déterminer si le commit est buggé ou pas.

Par ailleurs, vous devrez certainement modifier les droits du fichier:

$ chmod +x bin/behat

Enfin, vous devrez versionner ce script explicitement, car le dossier bin est généralement ignoré par Git dans le .gitignore

1
2
3
4
# .gitignore

/bin/*           #tout le dossier bin
!/bin/behat     #à l'exception de behat

Conclusion

Conclusion: bonne année! :)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>