Syndication : Atom 1.0  RSS 2.0
Blogs des développeurs   »   Blog de Bruno Orsier

Article complet: Du BDD vers le TDD, et vice-versa

04/02/2010

Permalink 17:00:26, Catégories: Tests unitaires, BDD, Récapitulatif Génie Logiciel, 978 mots   French (FR) , Bruno Orsier

[G. Logiciel] Du BDD vers le TDD, et vice-versa

Avant de passer au compte-rendu du dojo d'aujourd'hui, voici un nouvel éclairage sur l'articulation entre TDD et BDD, que j'emprunte à ce billet Behavior Driven Development with NBehave (trouvé grâce à cet autre billet Bien Tester une application Asp.net MVC sur le BDD par Guillaume Saint Etienne).

Je pense que dans les dojos précédents nous avons bien compris et bien pratiqué le cycle RED/GREEN/REFACTOR du TDD :

image

Nous voulons maintenant comprendre comment cela s'articule avec le BDD (partant du principe que nous sommes convaincus que cela vaut le coup de s'intéresser au BDD).

Je trouve que le schéma ci-dessous de Behavior Driven Development with NBehave illustre assez bien la manière de procéder, du BDD vers le TDD :

image

[Suite:]

Voici comment je comprends ce schéma :

  • On se laisse donc guider par une user story (ce qui évite un risque du TDD qui est de s'égarer sur des questions peu pertinentes pour l'utilisateur final, étant donné que l'on travaille très près du code).
  • La première étape est d'avoir des scénarios "Pending" - en Cucumber cela correspond au premier fragment de pont que Cucumber génère automatiquement.
  • En implémentant ces scénarios, on définit itérativement l'API des classes métier. Pour cela on suit un cycle RED/GREEN/REFACTOR au niveau de l'outil de BDD, et on ne cherche pas nécessairement à implémenter le comportement réel et final : on peut utiliser des comportements "codés en dur", ou simplistes - c'est ce qu'illustrait le dojo précédent finalement. C'est la partie "Stubbed Scenario" ci-dessus.
  • Une fois que l'API est stable, on implémente le comportement réel, et pour cela on pratique le RED/GREEN/REFACTOR en changeant d'outil : cette fois on fait du TDD avec un outil du type xUnit.
  • Et l'on obtient un test d'acceptance (sous forme de scénario exécutable) à la fin du processus.

Aujourd'hui dans le dojo nous avons travaillé essentiellement au niveau "Real Behavior", l'API de notre classe CalibrationCurve avait en effet été stabilisée grâce au travail de la dernière fois, nous n'avons pas eu à la retoucher. Par contre nous avons complètement reprogrammé son comportement interne.

Pour commencer à programmer, nous nous sommes appuyés sur le test unitaire suivant :

class LinearRegressionTest ? Test::Unit::TestCase

def setup
@curve = CalibrationCurve.new
@curve.ajoute_points(0, 10)
@curve.ajoute_points(10, 100)
end

def test_calcul_slope
assert_in_delta(0.111, @curve.calcule_slope, 0.001)
end

def test_calcule_ordinate
assert_in_delta(-1.111, @curve.calcule_ordinate, 0.001)
end

end

(désolé, pour d'obscures raisons le moteur de blog ne me laisse pas insérer le caractère "inférieur", et j'ai mis ≤ à la place dans les morceaux de code qui en avaient besoin)

On peut noter que nous n'avons pas inventé de nouvelles données du test, nous avons simplement utilisé les données déjà présentes dans le scénario qui échouait à la fin de la dernière séance :

Avec le recul, on peut se demander si ce test unitaire n'est pas complètement redondant avec le scénario, et s'il présente vraiment de l'intérêt ! En effet nous exerçons l'API de notre classe exactement comme dans le pont entre le scénario et la classe CalibrationCurve.

Par contre, en programmant les calculs nous avons identifié un cas limite (un seul point fourni par l'utilisateur) et nous avons dû prendre une décision sur le comportement attendu. Voici un test unitaire qui couvre ce cas :

class LinearRegressionTestLimite ? Test::Unit::TestCase

def setu<
@curve = CalibrationCurve.new
@curve.ajoute_points(0, 0)
end

def test_slope_should_be_zero
assert_equal(@curve.calcule_slope, 0)
end

def test_ordinate_should_be_zero
assert_equal(@curve.calcule_ordinate, 0)
end

end

On peut alors se demander si cette information doit rester "cachée" dans un test unitaire, ou bien s'il faut la faire remonter à l'utilisateur, via un complément de scénario. La décision dépend surement d'un dialogue avec les utilisateurs : est-ce que ce cas limite est pertinent pour eux ou non ? est-ce qu'ils sont intéressés par spécifier le comportement attendu ?

En tout cas ce petit exemple nous indique déjà que le schéma ci-dessus Story ==> Acceptance Test n'est pas si linéaire que cela : en travaillant en TDD, nous pouvons découvrir des compléments de scénarios !

Sur un point plus technique, nous avons également éprouvé le besoin d'un test unitaire pour nous assurer du bon fonctionnement de notre extension du module Enumerable, auquel nous avons ajouté une méthode mean comme ci-dessous :

module Enumerable
def mean
sum = self.inject(0.0) do |sum, xi|
sum = sum + xi
end
return sum / self.size
end
end

Comme nous ne connaissions pas suffisamment le principe du inject, le test unitaire nous a rassuré sur le bon fonctionnement de notre extension mean :

class EnumerableTest ? Test::Unit::TestCase

def setup
@array = [10, 100]
end

def test_mean
assert_equal(55, @array.mean)
end
end

En ce qui me concerne, je garderais comme test unitaire uniquement ce dernier point très technique, et je me contenterais du scénario et d'un complément de scénario pour le cas limite. Et vous, comment feriez-vous ?

Pour finir, le code de CalibrationCurve qui passe avec succès nos scénarios et tests unitaires :

class CalibrationCurve

def initialize
@x = []
@y = []
end

def compute
xbar = @x.mean
ybar = @y.mean
sx2= @x.map{|xi| (xi-xbar)**2}.mean

sum = 0
@x.each_with_index do |xi, i|
sum = sum + (@x[i]-xbar)*(@y[i]-ybar)
end
sxy = sum / @x.size

if sx2.abs != 0 then
@a = sxy / sx2
@b = ybar - @a*xbar
else
@a = 0
@b = 0
end
end

def ajoute_points (area,concentration)
@x.push(concentration)
@y.push(area)

compute
end

def calcul_concentration(area)
return (area - @b) / @a
end

def calcule_slope
return @a
end

def calcule_ordinate
return @b
end

end

Social Bookmarking:

                                     

Commentaires, Pingbacks:

Connectez-vous pour vous abonner à cet article:

Flux de commentaires pour cet article : Atom 1.0  RSS 2.0

Cet article n'a pas de Commentaires/Pingbacks pour le moment...

Vous devez être identifié pour poster un commentaire.

Liste des blogs

Blog de Bruno Orsier

Rechercher

<  Février 2012  >
Lun Mar Mer Jeu Ven Sam Dim
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29        

Syndiquez ce blog XML

Articles :

Commentaires :

 
 
 
 
Partenaires

Hébergement Web