Un problème rencontré récemment
Quelque chose que vous auriez souhaité savoir plus tôt
Mettre le titre de l'histoire sur le post it
A partir de quel moment cette décision n'était plus réversible ?
Quand est ce que le problème a été remonté ?
Quel a été le coût ?
Mauvaise information communiquée à un client externe ?
Deadline non respectée ?
Perte financière ?
Code écrit pour rien ?
Comment auriez vous pu avoir l'information manquante
A qui auriez vous pu parler ?
Où était l'information
Comment auriez vous pu assouplir vos engagements ?
BDD n'est pas nécessairement un outil d'automatisation
BDD n'est pas associée aux tests bout en bout
BDD n'est pas associée aux tests sélénium
BDD n'est pas un outil
Avoir les conversations est plus important que les écrire ou les automatiser - Liz Keogh
Black box testing (Weinberg and Gause, 1989)
Exemples automatisés (Ward Cunningham, 1996)
DDD (Eric Evans, 2004)
1995: Ward Cunningham & Kent Beck
Les 3 règles (Bob Martin, 2005):
Il est interdit d'écrire du code de production à moins que cela soit dans le but de faire passer un test en échec.
Il est interdit d'écrire plus que ce qui est nécessaire d'un test unitaire afin qu'il entre en échec; les échecs de compilations comptent pour des échecs.
Il est interdit d'écrire plus que ce qui est nécessaire du code de production afin de faire passer le test en échec.
Spécifications des interactions
Outil de design interne et externe incrémental
Écrites par les programmeurs
Pour les programmeurs
J'écris mon code en premier puis les tests
Les tests me permettent de vérifier que mon implémentation est correcte
Mes tests sont un filet de sécurité
Je peux modifier mon code avec confiance
Si j'écris mes tests avant l'implémentation alors je finis par écrire moins de code
De plus j'arrive à écrire un code plus facilement testable ce qui améliore mon design
Cette manière de travailler permet de concentrer mon attention sur la tache en cours
Avec le temps qui passe mes tests me servent de documentation
Écrire des tests revient à designer/spécifier l'API publique que je souhaiterais avoir
J'organise mon travail en commençant sur une phase de réflexion
En écrivant ces spécifications je spécifie le comportement attendu entre l'objet que je spécifie et ses collaborateurs
Par où commencer ?
Que faut-il tester ?
Comment appeler les tests ?
Que faut-il ne pas tester ?
Mais...les tests...c'est pour les testeurs !!!
Les noms des méthodes de test doivent être des phrases
import junit.framework.*;
public class TestMath extends TestCase {
public void testAdd() {
int num1 = 3;
int num2 = 2;
int total = 5;
int sum = 0;
sum = Math.add(num1, num2);
assertEquals(sum, total);
}
}
import junit.framework.*;
public class TestMath extends TestCase {
public void testAddingTwoNumberReturnTheirSum() {
int num1 = 3;
int num2 = 2;
int total = 5;
int sum = 0;
sum = Math.add(num1, num2);
assertEquals(sum, total);
}
}
Hypothèse de Sapir-Whorf/PNL
Les mots et les langages influent sur notre pensée
Introduction du mot "should" à la place de "test"
Le test se lit maintenant comme une spécification
import junit.framework.*;
public class TestTimer {
public void testStartStop() throws Exception {
Timer timer = new Timer();
timer.start();
Thread.sleep(10);
timer.stop();
assertTrue(timer.elapsedTimeMillis() > 0);
}
}
public class TimerSpec {
public void shouldMeasureAPeriodOfTime() throws Exception {
Timer timer = new Timer();
timer.start();
Thread.sleep(10);
timer.stop();
Verify.that(timer.elapsedTimeMillis() > 0);
}
}
Avantages de "should"
Permet de nommer facilement les méthodes de test
Permet d'appliquer SRP
Permet de remettre en cause les tests
Aide à comprendre les rapports de tests
Tout cela ressemble fortement à de l'analyse...
Scénario: Effectuer un retrait avec succès
# Le contexte
Etant donné que je possède '100€' sur mon compte
# Les événements
Quand je demande '20€'
# Le résultat attendu
Alors '20€' doivent être distribués
Le développement est tiré par le besoin
Tout artefact existe car il provient d'un besoin plus haut dans le système
Permet de limiter le travail en cours
Joshua Kerievsky (Refactoring to Patterns)
Communities that do StorytestDrivenDevelopment (SDD) work collaboratively on storytests. A domain expert may write some storytest details on a whiteboard while a QA person looks on and asks questions . Next, developers collaborate with the QA person and domain expert to understand and then automate the storytest. Lots of dialogue between members of the customer and developer communities happens throughout this process.
BDD is a second-generation, outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology.It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.
Second-generation
Comment tester ?
Comment documenter ?
Outside-in and pull based
Inspiré de lean
Développement tiré par le besoin
Besoin exprimé collaborativement sous forme d'exemples
On produit que ce qui est nécessaire
On produit quand c'est nécessaire
Agile
Itérations
Équipes multi-compétentes
Travail en tranches
High-Automation
Outillage
Vérification automatisée
Feedback sur travail en cours
Multiple stake holder (Kaizen)
Pousser la collaboration sur les 3 axes:
Métier
Technique
Personnes et les processus
Multiple scale
Standardiser ATDD,BDD,TDR,Example Driven Development,Story Testing Driven Development, Executable specifications
The fact that the same practices have so many names reflects the huge amount of innovation in this field at the moment. It also reflects the fact that the practices described in this book impact the ways teams approach specifications, development, and testing. To be consistent, I had to choose one name. I settled on Specification by Example*.
*Terme créé originellement par M.Fowler, 2002
Ambiguïtés
Il a trouvé un avocat
Amphibologie
Elle est sortie en pleurant du café
Illusions
Comment le client l'a expliqué
Comment le chef de projet l'a compris
Comment l'analyste l'a conçu
Comment les développeur l'ont implémenté
Ce que les testeurs ont reçu
Comment les commerciaux l'ont décrit
Comment le projet a été documenté
Ce que la production a installé
Ce que le client a été facturé
Le support client qui a été fourni
Comment le marketing la vendu
Ce dont le client avait vraiment besoin
Je possède la vision du produit, connaît les conditions d'utilisations normales, réponds aux questions.
Je clarifie le besoin et vérifie la faisabilité technique (implem+automat)
Je sais où appuyer pour que ça fasse mal.
Les individus et les échanges plutôt que les processus et les outils
Nous voulons augmenter le nombre de commandes. Nous allons donc accorder 10% de remises sur toutes les premières commandes.
public void CalculateDiscount(Order order){ if(order.Customer.IsNew) order.FinalAmount = Math.Round(order.Total*0.9); }
Register as "newuser" Go to "/catalog/search" Enter "ISBN-0955683610 Click "Search" Click "Add to cart" Click "View Cart" Verify "Subtotal" is "30"
Trouver un langage commun (DDD)
qui s'abstrait de la technologie
le QUOI avant le COMMENT
Supprimer la différence d’impédance entre BA/DEV/Test
le même langage à tous les niveaux
Apprendre ensemble/Découvrir ensemble le comportement de l'application
Processus ayant pour but d'arriver à une vision commune
Etant donné un utilisateur qui n’a jamais commandé
Quand l’utilisateur met un livre dans son panier
Alors il faut lui accorder une remise de 10% sur le montant total du panier
Réalisée avant l'atelier
Avec responsable métier, analystes
Sortir les premiers exemples d'utilisation
Le but est de préparer les futures discussions
L'idéal: 1 dev, 1 testeur, 1 BA
Rotations de paires selon maturité
Idéalement une personne spécialisée dans le domaine de la fonctionnalité
Acteurs préparés à l'avance
Noter une version "allégée" des scénarios
Equipe entière selon maturité
Afin de 'valeur'
En tant que 'role'
Je veux 'fonctionnalité'
Comment cela doit fonctionner ?
Pourquoi cela doit il fonctionner comme cela ?
Qui a besoin de cette fonctionnalité
Que se passerait il si cette fonctionnalité était enlevée?
Quelle est la valeur pour les utilisateurs
Les 5 pourquoi
Trouver des nouveaux scénarios à travers la collaboration et le brainstorming
Etant donné 'essayer de trouver différents contextes'
Quand 'utilisation de la fonctionnalité'
Alors 'essayer de trouver des vérifications alternatives'
Découvrir ce que nous ne savons pas
Découvrir les choses critiques qui pourraient être oubliés
Processus d'apprentissage collaboratif
Nous voulons proposer aussi à nos client la livraison à domicile
Existe il des cas où il ne faut pas proposer la livraison à domicile ?
Oui il ne faut pas la proposer pour les adresses de livraisons qui ne se situent pas sur paris
D'accord, il faut donc proposer cette option uniquement pour les adresses ayant pour code postal 75XXX
Attention, certains pays comme l’Italie ont des codes postaux de la forme 75XXX, mais bien sûr il ne faut pas proposer cette option en italie
OK, je pense qu'il faudrait écrire un scénario supplémentaire pour ce cas-ci afin de montrer que l'option n'est disponible qu'en France et que pour les code postaux situés sur Paris
Nous voulons offrir la livraison gratuite pour certains produits comme certains livres par exemple
Est-ce que cette livraison gratuite est disponible qu'importe le lieu de livraison ?
En effet il ne faut pas proposer la livraison à domicile en dehors de la France.
Que se passe-t-il si j’achète un livre avec livraison gratuite et un sans livraison gratuite ?
Dans ce cas on bénéficie toujours de la livraison gratuite pour l'ensemble des livres
Que se passe-t-il si je commande un livre et un frigo, certainement la livraison ne pas plus être gratuite non ?
Mhmmm nous n'avons pas pensé à ce cas, je dois en discuter et je reviendrais vers vous concernant ce scénario.
Etant donné que je possède 100€ sur mon compte
Quand je demande 20€
Alors 20€ doivent être distribués
Etant donné que je possède 100€ sur mon compte
Quand je demande 20€
Alors je dois récupérer ma carte
Etant donné que je possède 100€ sur mon compte
Quand je demande 20€
Alors mon compte doit être débité de 20€
Un exemple concret vaut mieux qu'une explication générale et abstraite
Les clients ne doivent pas pouvoir saisir de numéro de carte bleue invalide
Laisse des questions en suspens: Qu'est-ce qu'un numéro de carte invalide ? Comment l'utilisateur doit être notifié ?
Une meilleure version:
Scénario: saisie de numéro de carte bleue incorrect
Etant donné que le client a saisit un numéro de carte bleu avec une longueur inférieure à 16 chiffres
Quand il valide sa saisie
Alors un message doit lui indiquer que sa saisie est incorrecte
Simple et souple
Gherkin n'impose que les mots clés Given/When/Then
Tout le reste peut être écrit en langage naturel !
Écriture et lecture à la portée de tous
Peuvent être remises en cause
Implication du métier en leur offrant une meilleure transparence sur l'implémentation des fonctionnalités
Ludique et facilite le brainstorming
Permet d'améliorer l'esprit d'appropriation de l'équipe face au produit car on va spécifier ensemble
Permet de simuler la future utilisation de la fonctionnalité et dans voir les limites
Permet de découvrir de nouvelles options avant de s'engager dans une voie
L'équipe entière s'investit dans
La qualité du produit
L'automatisation des spécifications
La création d'une application facile à tester
La documentation de l'application
Working software over comprehensive documentation
Une documentation de l'application toujours à jour !
Des spécification versionnées grâce au contrôle de source
Donne un point de départ à TDD + lean code + Feature injection + YAGNI + KISS
Création d'un langage omniprésent (DDD, spec, code, test, conversations)
Servent de filet anti-régression
Permet de faciliter le refactoring car test boite noire
Donne un point de départ à TDD + lean code + Feature injection + YAGNI + KISS
Feature: Hello World
Scenario: Say hello
Given I have a hello app with "Howdy"
When I ask it to say hi
Then it should answer with "Howdy World"
public class HelloStepdefs {
private Hello hello;
private String hi;
@Given("^I have a hello app with \"([^\"]*)\"$")
public void I_have_a_hello_app_with(String greeting) {
hello = new Hello(greeting);
}
@When("^I ask it to say hi$")
public void I_ask_it_to_say_hi() {
hi = hello.sayHi();
}
@Then("^it should answer with \"([^\"]*)\"$")
public void it_should_answer_with(String expectedHi) {
assertEquals(expectedHi, hi);
}
}
Etant donné le produit 'Baskets' avec un prix de '50' euros et un poids de '300' g
Et le produit 'Jeans' avec un prix de '60' euros et un poids de '200' g
Et le produit 'T-shirt' avec un prixd '20 euros' et un poids de '50' g
Etant donné les produits:
|Nom |Prix (euros)|Poids (g)|
|Baskets|50 |300 |
|Jeans |60 |200 |
|T-shirt|20 |50 |
Fonctionnalité: Retrait d’un montant fixe
Le menu de retrait contient plusieurs montants
fixes afin que le client puisse retirer rapidement
Scénario:Retrait de 50€
Etant donné que je possède 500€ sur mon compte
Quand je retire 50€
Alors je dois recevoir 50€
Et mon compte doit posséder un solde de 450€
Scénario:Retrait de 100€
Etant donné que je possède 500€ sur mon compte
Quand je retire 100€
Alors je dois recevoir 100€
Et mon compte doit posséder un solde de 400€
Scénario:Retrait de 200€
Etant donné que je possède 500€ sur mon compte
Quand je retire 200€
Alors je dois recevoir 200€
Et mon compte doit posséder un solde de 300€
Fonctionnalité: Retrait d’un montant fixe
Le menu de retrait contient plusieurs montants
fixes afin que le client puisse retirer rapidement
Scénario:Retrait
Etant donné que je possède '<solde>'€ sur mon compte
Quand je retire '<retrait>'€
Alors je dois recevoir '<distribué>'€
Et mon compte doit posséder un solde de '<restant>'€
Examples:
|solde|retrait|distribué|restant|
|500 |50 |50 |450 |
|500 |100 |100 |400 |
|500 |200 |200 |300 |
Etant donné que j’essaie de créer un compte avec le mot de passe '<Mot de passe>'
Alors je devrais être notifié que le mot de passe est '<Validité>'
Examples: Trop court
Les mots de passe inférieurs à 4 caractères sont invalides
| Mot de passe | Validité |
| abc | invalide |
| ab1 | invalide |
Examples: Lettres et chiffres
Les mots de passe doivent contenir des lettres et des chiffres
| Mot de passe | Validité |
| abc1 | valide |
| abcd | invalide |
| abcd1 | valide |
Éviter les scénarios trop abstraits
Scénario: Montant total du panier
Etant donné un client avec 3 produits dans son panier
Alors il doit visualiser correctement le montant total
Scénario: Montant total du panier
Etant donné un client A
Et possédant un panier avec les produits B et C
Éviter les scénarios trop abstraits
Scénario: Montant total du panier
Etant donné un client avec les produits suivants dans son panier
|Nom du produit |Prix unitaire(euros)|Quantité|
|PS3 |250 |1 |
|Prince of persia|50 |1 |
Quand il consulte son panier flotant
Alors il doit visualiser un montant total de 300 euros
Faire apparaître des ID techniques
Scénario: Montant total du panier
Etant donné les produits suivants:
|Id|Produit |Prix unitaire|
|1 |PS3 |250 |
|2 |Prince of persia|50 |
Et le client suivant
|Id|Nom|
|1 |John|
Et le panier suivant:
|Id|ClientId|
|1 |1 |
Et avec les articles suivants:
|Id|PanierId|ArticleId|Quantité|
|1 |1 |1 |1 |
|2 |1 |2 |1 |
Lisibilité avant tout (scripts à bannir)
Ne pas exprimer les scénarios en terme d'UI
Découplé de l'UI afin de faciliter le changement
Sont généralement trop verbeux, ennuyants à lire et fragiles
Exprimez vos scénarios en terme métier
Éviter les tests workflow
=>Tester en terme d'UI ou de workflow masque l'intention, le lecteur doit refaire le chemin inverse
=>Le QUOI pas le COMMENT
Se concentrer sur comment l'application doit réaliser la fonctionnalité
Scénario: Les utilisateurs non connectés doivent être redirigés vers le formulaire de connexion
Etant donné l’utilisateur 'Pierre'
Et ayant pour adresses email 'pierre@yahoo.com'
Et ayant le mot de passe 'passw0rd'
Quand l’utilisateur visite la page 'http://localhost:5740/monsite'
Alors il doit être redirigé vers la page 'https://localhost:5740/monsite/login'
Quand il saisit le login 'pierre@yahoo.com' dans le champ '#login'
Et qu’il saisit le mot de passe 'passw0rd' dans le champ '#mdp'
Et qu’il valide le formulaire "#formLogin" de connexion
Alors il doit être redirigé vers la page 'http://localhost:5740/monsite/home'
Se concentrer sur ce que doit faire l'application et non pas comment elle le réalise
Scénario: Les utilisateurs non connectés doivent être redirigés vers le formulaire de connexion
Etant donné un utilisateur non connecté
Quand il essaie d’accéder à n’importe quelle page du site
Alors il doit être redirigé vers le formulaire de connexion
Scénario: Utilisateur ayant été redirigé vers le formulaire de connexion
Etant donné un utilisateur ayant été redirigé vers le formulaire de connexion
Quand il soumet ses identifiants
Alors il doit être redirigé vers la page dont il avait essayé d’accéder
Ne pas prendre le temps de bien nommer les scénarios
Scénario: 100 euros
Etant donné que le client possède pour plus de 100 euros de produits
Quand il consulte son récapitulatif de panier
Alors il doit visualiser une réduction immédiate de 10% sur le montant total
Faire un résumé du scénario
Scénario: Accorder une réduction de 10% pour les paniers de plus de 100 euros
Etant donné que le client possède pour plus de 100 euros de produits
Quand il consulte son récapitulatif de panier
Alors il doit visualiser une réduction immédiate de 10% sur le montant total
Permet de retrouver plus rapidement des scénarios d'intérêt
Permet par la suite d'aider à l'analyse des scénarios en échec
@nightly @web @summary @sprint10
Scénario: Accorder une réduction de 10% pour les paniers de plus de 100 euros
Etant donné que le client possède pour plus de 100 euros de produits
Quand il consulte son récapitulatif de panier
Alors il doit visualiser une réduction immédiate de 10% sur le montant total
Partir du happy path
Explorer à partir de là
Ces scénarios pourront servir de bases pour une autre spec plus orienté vérification.
Fast
Independent
Repeatable
Self Validating
Timely
Il n'y pas de bugs!
Mais que des scénarios oubliés !
Écrire les scénarios en solo
Écrire les scénarios avec seulement BA et testeur puis catapultage sur le développeur
Les 3 acteurs doivent toujours arriver à une étape de collaboration
Afin de développer le langage/compréhension/vision commune
Utiliser Specflow pour de simples TU
Feature: Stack
When a new stack is created
Then it is empty
When an element is added to the stack
Then that element is at the top of the stack
When a stack has N elements
And element E is on top of the stack
Then a pop operation returns E
And the new size of the stack is N-1
describe Hash do
before do
@hash = Hash.new({:hello => 'world'})
end
it "should return a blank instance" do
Hash.new.should == {}
end
it "hash the correct information in a key" do
@hash[:hello].should == 'world'
end
end
Livres
Attention
Pas que du bon sur le net!