jeudi 28 juillet 2011

Tester son code Ruby : Minitest



la programmation est une activité super intéressante. Chacun a sa métaphore préférée qui explique ce que signifie pour lui la programmation. Eh bien, pour moi "la programmation est tout au  sujet d'automatisation". 

Vous êtes devant votre ordinateur pour lui apprendre à faire automatiquement un travail que vous savez faire, mais que vous ne voulez pas faire encore et encore.

Quand on s'en rend compte, on cherche tout de suite des choses qui  peuvent être automatisées. Je n'aime pas me répéter encore et encore et encore. C'est ennuyeux et parfois énervant!
 Eh bien, il y a une tâche particulière qui est liée à la programmation et qui se répète tout le temps : "vérifier que votre logiciel fonctionne!" Donc une tâche à automatiser.

 > MINITEST 

Heureusement pour nous, Ruby a des outils fantastiques qui vous permettent de mettre en place des tests pour votre code que vous pouvez exécuter automatiquement. Vous pouvez vous éviter de perdre du temps et de l'effort en laissant l'ordinateur exécuter des milliers de tests à chaque fois que vous modifiez votre code. Je ne vais pas disserter sur les vertus du«Test Driven Development" dans cet article, mais écrire les premiers tests. Pour commener, nous allons prendre un petit peu de code pour travailler, et je vais vous montrer comment le tester en utilisant minitest.

Pour cet exercice, nous allons faire quelque chose de simple et nous concentrer sur les tests. Nous allons écrire une classe Ruby appelée Membre. Il va falloir un tas de fonctionnalités, mais voici les deux premières méthodes, nous aurons besoin:
-> un membre peut ajouter d'autres membres en tant qu'amis-> un membre peut savoir combien d'amis il a-> quand il est nouveau, le nombre de ses amis est 0-> quand il supprimer sa liste d'amis, il revient à 0 ami.
Très simple non ? Pour écrire ces tests nous allons utiliser une librairie Ruby appelée minitest. Elle est native dans Ruby1.9 mais si vous utiliser 1.8 installez la en faisant :
> gem install minitest
Ecrivons notre premier test !

require 'minitest/autorun'

class TestMembre < MiniTest::Unit::TestCase
  def setup
    @membre = Membre.new
  end
  def test_pas_d_amis
    assert_equal 0, @membre.nombre_amis
  end
end

Ok! Il ya beaucoup de choses ici. Prenons ligne par ligne. Sur la première ligne, nous avons un «require». La partie 'minitest/autorun' comprend tout ce dont vous avez besoin pour exécuter vos tests, automatiquement. 
Tout ce dont nous avons besoin pour faire tourner nos tests est de taper "ruby membre.rb". Mais regardons le reste du fichier avant de faire cela. La prochaine chose que nous faisons est écrire une classe qui hérite de l'une des classes de base de minitest. Voilà comment fonctionne minitest, en exécutant une série de TestCases. Minitest permet également les tests de groupe et en plusieurs fichiers.
Dans notre TestCase nous avons 2 méthodes : setup et test_pas_d_amis. La méthode setup est exécutée automatiquement et avant chaque test. Elle permet de préparer le terrain pour réaliser les tests. La deuxiéme méthode permet de vérifier qu'un nouveau membre n'a pas encore d'amis.
Lançons notre test et voyons ce que ça donne :
> ruby membre.rb

Loaded suite membre
Started
E
Finished in 0.000853 seconds.

1) Error:
test_pas_d_amis(TestMembre):
NameError: uninitialized constant TestMembre::CashRegister
membre.rb:5:in `setup'

1 tests, 0 assertions, 0 failures, 1 errors, 0 skips

Test run options: --seed 36463
Waaaaw cool ! Comme vous pouvez le voir nous avons un test et un échec. Nous savons que les classes Ruby sont des constantes et il en manque une ici. Ecrivons cette classe alors.
class Membre
end

Relançons le test :

1) Error:
test_pas_d_amis(TestMembre):
NoMethodError: undefined method `nombre_amis' for #<Membre:0x00000101032a80>
membre.rb:9:in `test_pas_d_amis'

ça avance ! et cette fois ça nous dit qu'il manque une méthode.

class Membre
  def nombre_amis
  end
end
Relançons le test encore une fois.

1) Failure:
test_pas_d_amis(TestMembre) [membre.rb:9]:
Expected 0, not nil.
Erreur claire : On s'attend à 0 pas à nil

def nombre_amis
  0
end

Ok cool. Le test passe mais notre total est toujours fixe (0). Vérifions que quand on ajoute des amis, le nombre d'amis change.

def test_calculer_nombre_amis
  @membre.ajouter 1
  @membre.ajouter 2
  assert_equal 3, @register.nombre_amis
end
A ce stade, la visoin doit être un peu plus claire pour vous. Lançons nos tests encore une fois


Loaded suite membre
Started
.E
Finished in 0.000921 seconds.

1) Error:
test_calculer_nombre_amis(TestMembre):
NoMethodError: undefined method `ajouter' for #<Membre:0x00000101031838>
membre.rb:13:in `test_calculer_nombre_amis'

2 tests, 1 assertions, 0 failures, 1 errors, 0 skips

Test run options: --seed 54501
Ok! vous voyez le ".E" sur la 3iéme ligne du résultat ? Ben ça veut dire qu'on a eu 2 tests, un réussi '.' et un échoué "E".
Ajouter la méthode qui manque et continuons .


def ajouter(ami)
  @amis << ami
end


Oups ! le @amis n'a pas encore été défini :) Lancez le test et il vous le dira.



def initialize
  @amis = []
end
Avec ce constructeur, nous n'aurons plus ce genre de problèmes.


Relancez le test et vous aurez une erreur : le nombre d'amis est toujours 0 alors qu'il devait passer à 3 .
Corrigeons ce petit souci:



def nombre_amis
  @amis.inject(0, &:+)
end

C'est fait. Alors pour vérifier que vous avez bien compris le truc, écrivez le dernier test : supprimer_liste_amis.


.....


Alors c'est quoi l'intérêt d'écrire les tests en premier ? Ben ça vous permet d'écrire le code qu'il faut pour répondre aux spécifications que contient votre cahier de charges d'une part et de vous assurer que votre logiciel fait exactement ce pour quoi il a été écrit. 


Les mises à jour du code deviennent chose simple. Et quand il a un problème on sait que c'est le nouveau code qui a cassé quelque chose surtout que le "code base" était digne de confiance.


Dans la suite de cette série d'articles sur les tests, nous allons intégrer les tests à des applications Rails et Sinatra.






Aucun commentaire:

Enregistrer un commentaire