Living Documentation
Comment coder et
transmettre
ou
@guillaume_agile
Documenter? Pourquoi?
Qui est le public visé?
Qui va relire?
Que voulons nous dire?
Que voulons nous laisser comme trace?
Documenter? A quel prix?
It is more important to
communicate to humans with
your code
than to communicate with a
machine.
Bob C. Martin
Quels écueils ?
➔ Avoir de la documentation non alignée et pas à jour
◆ dès qu’une ligne de code change, le comportement
change et la documentation est obsolète
◆ dès qu’une ligne de spécification change, aussi
◆ on sent le besoin d’un lien entre les deux !!!
➔ Documentation qui n’a pas public
➔ Documentation illisible pour son public
➔ De la documentation inutile
◆ données que l’on a déjà ou qui se trouvent ailleurs
(redondance)
◆ Informations inutiles (OSF)
“La vérité est dans
le code”
Est ce bien le code qu’il faut documenter?
Le code, s’il est bien écrit peut être très expressif:
➔ les noms sont importants:
◆ variables
◆ fonctions
◆ classes
➔ Sujets, verbes et compléments forment des phrases
➔ Les fonctions font !
➔ Les fonctions sont fonctionnelles
➔ A l’intérieur du code, qui lit? qui va comprendre? qu’est
ce qu’il y a comprendre?
➔ Essayez: fluent programming
➔ Essayez: functionnal programming
➔ Pensez à HATHEOAS
DRY !!!
➔ Uniquement des informations pertinentes
➔ Aucune redondance
➔ De l’information utile
➔ Qui donne du sens
➔ Qui soit compréhensible
Le code c’est “COMMENT”
Ce serait bien de documenter le POURQUOI ? !!!
Relisez 3 fois le code pour savoir ce qu’il contient déjà...
et ré-écrivez le!
Redondant ?
//ceci est une calculateur qui calcule
public class Calculateur
{
//renvoit le résultat de a additioné à b
public double Additionne (double a, double b )
{
}
Redondant ?
//ceci est une calculateur qui calcule
public class Calculateur
{
//renvoit le résultat de a additioné à b
public double Additionne (double a, double b )
{
return a - b;
}
// mais c’est pas grave ;)
Compliqué ? Laid ?
static void Main()
{
try
{
// Iterate through all plug-ins.
foreach (var filePath in Directory.GetFiles(Constants.PluginPath, Constants.PluginSearchPattern))
{
// Create the plug-in AppDomain setup.
var pluginAppDomainSetup = new AppDomainSetup();
pluginAppDomainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// Create the plug-in AppDomain with the setup.
var plugInAppDomain = AppDomain.CreateDomain(filePath, null, pluginAppDomainSetup);
// Pass the plug-in file path to the AppDomain
var pluginContext = new PluginContext { FilePath = filePath };
plugInAppDomain.SetData(Constants.PluginContextKey, pluginContext);
// Execute the loader in the plug-in AppDomain's context.
// This will also execute the plug-in.
plugInAppDomain.DoCallBack(PluginCallback);
// Retrieve the flag if the plug-in has executed and can be deleted.
pluginContext = plugInAppDomain.GetData(Constants.PluginContextKey) as PluginContext;
// Unload the plug-in AppDomain.
AppDomain.Unload(plugInAppDomain);
// Delete the plug-in if applicable.
if (pluginContext != null && pluginContext.CanDeletePlugin)
{
File.Delete(filePath);
}
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
public class JeFaisDesTrucsSuper {
public int JeLeFaisAvecUnUtilisateur() {
//je regarde si je connais l'utilisateur
if (e.AddedItems.Count > 0)
{ details.InitializeWith(e.AddedItems[0] as Model.Definition.AppointmentOrTask);
MainGrid.ColumnDefinitions.Add(DetailsAreVisible); }
else MainGrid.ColumnDefinitions.Add(DetailsAreInvisible);
//je vais lui demander son année de naissance
if (Sort == null) return false;
Sort.IsAcsending = Sort.IsAcsending.HasValue && !Sort.IsAcsending.Value;
var items = List.ItemsSource.Cast<object>();
List.ItemsSource = Sort.IsAcsending.Value
? items.OrderBy(o => SortBy.GetValue(o, null))
: items.OrderByDescending(o => SortBy.GetValue(o, null));
//je vais mémoriser son choix
//je vais générer une chiffre au hasard
//je vais demander l'age du captain
//je vais additionner tout cela
//et dire à l'utilisateur que j'ai trouvé son chiffre porte bonheur
//je vais le mémoriser aussi
using (var dbCommand = Sqlbase.CreateCommand (connection,
defautCommandTimeOutForSelectStatement, selectCommand, CommandType.Text, spParams))
{
Log.Write (LogLevel.Verbose, "Sql.Select: {0} with params: {1}", selectCommand.Replace
("rn", " "), DisplayParamsDetails (spParams));
IDbTransaction transaction = null;
if (isolation != IsolationLevel.Unspecified) {
transaction = connection.BeginTransaction (isolation);
dbCommand.Transaction = transaction;
}
}
//et je renvoie, on ne sait jamais
return x;
}
}
//fin de ma fonction
Refactoring
Single Responsability
Nommage intelligible et intelligent
Qu’est ce qui est isolable et testable?
public class JeFaisDesTrucsSuperMaisEnMieux
{
public int JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui,
IStockeDesTrucs stockage)
{
//je regarde si je connais l'utilisateur
var esTuConnu = ui.Utilisateur.DemandeIdentite();
if (esTuConnu)
return -1;
//je vais lui demander son année de naissance
var anneeNaissance = ui.QuelEstTonAnneeDeNaissance();
//je vais mémoriser son choix
var resultatStockage = stockage.Sauvegarde( anneeNaissance,
ui.Utilisateur.IdentifiantUniversel );
public class JeFaisDesTrucsSuperMaisEnMieux
{
public int JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui,
IStockeDesTrucs stockage)
{
var esTuConnu = ui.Utilisateur.DemandeIdentite();
if (esTuConnu)
return -1;
var anneeNaissance = ui.QuelEstTonAnneeDeNaissance();
var resultatStockage = stockage.Sauvegarde( anneeNaissance,
ui.Utilisateur.IdentifiantUniversel );
Et quand on en a marre de lire le code..
Quelle serait la place pour une documentation efficace?
Un document électronique qui n’est pas le code mais “entre” le code et les
spécifications...
Mais bon sang, c’est bien sûr....
NOS AMIS LES TESTS
public class JeFaisDesTrucsSuperMaisEnMieux
{
public int JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui,
IStockeDesTrucs stockage)
{
var esTuConnu = ui.Utilisateur.DemandeIdentite();
if (esTuConnu)
return -1;
var anneeNaissance = ui.QuelEstTonAnneeDeNaissance();
var resultatStockage = stockage.Sauvegarde( anneeNaissance,
ui.Utilisateur.IdentifiantUniversel );
[TestFixture ()]
public class TesteLeStockage
{
[Test ()]
public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur ()
{ //arrange
int valeurAStocker = 101;
int IdUser = 1;
IJeStocke sut = depencyInjection.Get<IJeStocke> ();
//act
var resultatStockage = sut.Stocke (valeurAStocker, IdUser);
sut.ResetConnection();
int verificationValeur = 0;
var resultatLecture = sut.Lecture (out verificationValeur, IdUser);
//assert
Assert.That(verificationValeur , Is.EqualTo(valeurAStocker));
}
}
Les Tests Unitaires
• Ils décrivent ce qui est attendu
• Ils “déclenchent” le code
• Ils sont organisés avec le code
• Si le comportement du code change, au
moins un de mes tests passe au rouge
(sinon j’ai un souci...)
• Donc ils vivent avec le code
• Et si mes tests pouvaient être écrits dans un
langage compréhensible par tous...
IT’S ABOUT TELLING A STORY
[TestFixture ()]
public class TesteLeStockage
{
[Test ()]
public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur ()
{
int valeurAStocker = 101;
int IdUser = 1;
IJeStocke sut = depencyInjection.Get<IJeStocke> ();
var resultatStockage = sut.Stocke (valeurAStocker, IdUser);
sut.ResetConnection();
int verificationValeur = 0;
var resultatLecture = sut.Lecture (out verificationValeur, IdUser);
Assert.That(verificationValeur , Is.EqualTo(valeurAStocker));
}
}
Behaviors Driven Tests
Une syntaxe simple et légère
• Given
• When
• Then
Des utilitaires pour faire tourner les tests basés sur *Unit
* au choix: J/N/JS/x/....
Pas la peine d’en faire plus!
Scenario: Le Stockage Doit Memoriser Une Valeur Pour
Un Utilisateur
Given j’initialise correctement mon test
And j’ai un utilisateur ‘1’ connecté
When je sauve la valeur ‘101’
And je réinitialise le système
And je lis la valeur stockée
Alors le résultat de lecture est ‘101’
IT’S ABOUT BEING FORMAL
Scenario: Le Stockage Doit Memoriser Une Valeur Pour
Un Autre Utilisateur
Given j’initialise correctement mon test
And j’ai un utilisateur ‘222’ connecté
When je sauve la valeur ‘33333’
And je réinitialise le système
And je lis la valeur stockée
Alors le résultat de lecture est ‘3333’
IT’S ABOUT BEING MACHINE
READABLE
Executable Steps
[Given(@"j’initialise correctement mon test")]
public void GivenTestInit()
{
IJeStocke sut = depencyInjection.Get<IJeStocke> ();
}
Executable Steps (2)
[Given(@"j’ai un utilisateur ‘(.*)’ connecté")]
public void GivenTestInit(string x)
{
int IdUser = int.Parse( x );
}
Regular expressions
Pour l’instant la seule manière de passer du
langage naturel à une expression comprise
par la machine...
Des idées pour le futur ?
Executable Steps (2)
[Given(@"j’ai un utilisateur ‘[([0-9]+)]’ connecté")]
public void GivenTestInit(int x)
{
int IdUser = x ;
}
Step to Step
[Given(@"j’ai un utilisateur ‘[([0-9]+)]’ connecté")]
public void GivenTestInit(int x)
{
int IdUser = x ;
ScenarioContext.Current.Set(IdUser, “IdUser”);
}
Step to Step
[When(@" je sauve la valeur ‘‘[([0-9]+)]’’")]
public void WhenValueIsSaved(int valeurAStocker)
{
var IdUser = ScenarioContext.Current.Get<int>(
“IdUser”);
var resultatStockage = sut.Stocke (valeurAStocker,
IdUser);
Assert.That (resultatStockage, Is.True);
}
IT’S ABOUT OBJECTS
Talking to objects
Objects = instance de classe
Class = abstraction, concept
Interface = contrat, capacité de comportement
Inter-active Steps
[When(@" je sauve la valeur ‘‘[([0-9]+)]’’")]
public void WhenValueIsSaved(int valeurAStocker)
{
var IdUser = ScenarioContext.Current.Get<int>( “IdUser”);
var sut = ScenarioContext.Current.Get<IJeStocke>
( “CurrentTestStorage”);
var resultatStockage = sut.Stocke (valeurAStocker, IdUser);
Assert.That (resultatStockage, Is.True);
}
public interface IJeStocke
{
int Stocke (int valeur, int identifiantUniversel);
int Lecture (out int valeur, int
identifiantUniversel);
}
IT’S ABOUT BEING HUMAN
[TestFixture ()]
public class TesteLeStockage
{
[Test ()]
public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur ()
{
int valeurAStocker = 101;
int IdUser = 1;
IJeStocke sut = depencyInjection.Get<IJeStocke> ();
var resultatStockage = sut.Stocke (valeurAStocker, IdUser);
sut.ResetConnection();
int verificationValeur = 0;
var resultatLecture = sut.Lecture (out verificationValeur, IdUser);
Assert.That(verificationValeur , Is.EqualTo(valeurAStocker));
}
}
Scenario:
LeStockageDoitMemoriserUneValeurPourUnUtilisateur
Given j’initialise correctement mon test
And j’ai un utilisateur ‘222’ connecté
When je sauve la valeur ‘33333’
And je réinitialise le système
And je lis la valeur stockée
Alors le résultat de lecture est ‘3333’
Interfaces : the human side of objects
La documentation à travers les spécifications
exécutables est le reflet des interfaces objets
dans le monde des humains.
public interface IJeStocke
{
int JeSauve (int valeur, int identifiantUniversel);
int JeLis (out int valeur, int identifiantUniversel);
}
public interface IEntrepot
{
int JeSauve (int valeur, int identifiantUniversel);
int JeLis (out int valeur, int identifiantUniversel);
}
public class NoSqlQuiPoutre : IEntrepot
{
public int JeSauve (int valeur, int identifiantUniversel)
{ ............;}
public int JeLis (out int valeur, int identifiantUniversel)
{ ............;}
}
AI JE BESOIN DE COMMENTAIRES ???
[TestFixture ()]
public class TesteLeStockage
{
[Test ()]
public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur ()
{
int valeurAStocker = 101;
int IdUser = 1;
IJeStocke sut = depencyInjection.Get<IJeStocke> ();
var resultatStockage = sut.JeSauve (valeurAStocker, IdUser);
sut.ResetConnection();
int verificationValeur = 0;
var resultatLecture = sut.JeLis (out verificationValeur, IdUser);
} }
Change the way you code
What are interfaces (really) ?
Du rôle des interfaces
Ainsi « l’interface devient le produit et l’objet du service ». Elle n’est
plus un espace de commande utilisable, elle est la représentation
du service, son usage et sa finalité symbolique, cognitive et
esthétique.
http://www.nodesign.net/blog/vers-un-confort-numerique-moderne/
Les Interfaces doivent être ergonomiques
https://goo.gl/WUEh48
BDD: It's about Behaviour
BACKGROUND + GIVEN : Environnement + Setup
WHEN: ACTION
• Sujet
• Verbe
• Complement
THEN: RESULTAT ATTENDU
• Sujet
• Etat
END : Environnement rétalbi
IT’S ABOUT BEING MORE HUMAN
READABLE
Scenario:
LeStockageDoitMemoriserUneValeurPourUnUtilisateur
Given j’initialise correctement mon test
And j’ai un utilisateur ‘1’ connecté
When je sauve la valeur ‘101’
And je réinitialise le système
And je lis la valeur stockée
Alors le résultat de lecture est ‘101’
Scenario:
LeStockageDoitMemoriserUneValeurPourUnUtilisateur
Given j’initialise correctement le système de stockage
And j’ai un utilisateur ‘1’ connecté
When je sauve la valeur ‘101’ pour cet utilisateur
And je réinitialise le système de stockage
And je lis la valeur stockée pour l’utilisateur connecté
Alors le résultat de lecture est ‘1’
Inter-active Steps
[When(@" je sauve la valeur ‘‘[([0-9]+)]’’")]
public void WhenValueIsSaved(int valeurAStocker)
{
var IdUser = ScenarioContext.Current.Get<IUtilisateur>(
“User”);
var sut = ScenarioContext.Current.Get<IJeStocke>
( “CurrentTestStorage”);
var resultatStockage = sut.Stocke (valeurAStocker, IdUser);
Assert.That (resultatStockage, Is.True);
}
IT’S ABOUT CODE BEING MORE
HUMAN READABLE
public interface IEntrepot
{
int JeSauve (int valeur, IUtilisateur utilisateur);
int JeLis (out int valeur, IUtilisateur utilisateur);
}
public interface IUtilisateur
{
bool DemandeIdentite (ICredential credits);
int DonneAnneeNaissance ();
}
IT’S ABOUT TDD
Writing Test First
Feature: identification avec des jetons
Scenario: Un utilisateur est identifié
Given j’initialise correctement mon test
And j’ai un jeton égal à x10103
When je demande à identifier l’utilisateur avec ce jeton
Alors le résultat est ok
public class UtilisateurAJetons : IUtilisateur
{
#region IUtilisateur implementation
bool IUtilisateur.DemandeIdentite (Icredential credits)
{
throw new NotImplementedException ();
}
int IUtilisateur.DonneAnneeNaissance ()
{
throw new NotImplementedException ();
}
#endregion
}
Reusing Tests
Feature: identification avec Facebook
Scenario: Un utilisateur est identifié
Given j’initialise correctement mon test
And j’ai un jeton égal à FB44103
When je demande à identifier l’utilisateur avec ce jeton
Alors le résultat est ok
public class UtilisateurFacebook : IUtilisateur
{
#region IUtilisateur implementation
bool IUtilisateur.DemandeIdentite (Icredential credits)
{
throw new NotImplementedException ();
}
int IUtilisateur.DonneAnneeNaissance ()
{
throw new NotImplementedException ();
}
#endregion
}
IT’S ABOUT REFACTORING
Est ce que mon test est lisible ?
⇥
Est ce que j’ai bien ré-utilisé mes steps ?
⇥
Est ce que j’ai généralisé les bons concepts ?
⇥
Design des interfaces et des objets
IT’S ABOUT REUSING
Scenario:
LeStockageDoitMemoriserUneValeurPourUnUtilisateur
Given j’initialise correctement le système de stockage
And j’ai un utilisateur ‘1’ connecté
When je sauve la valeur ‘101’ pour cet utilisateur
And je réinitialise le système de stockage
And je lis la valeur stockée pour l’utilisateur connecté
Alors le résultat de lecture est ‘1’
Scenario: L’ Utilisateur doit pouvoir saisir une année de
naissance
Given j’initialise correctement le système graphique
And j’ai un utilisateur ‘1’ connecté
When l’ utilisateur saisi la valeur ‘1970’
Alors le résultat de Utilisateur[1].AnnéeDeNaissance est
‘1970’
Scenario: L’ Utilisateur doit pouvoir saisir une année de
naissance
Given j’initialise correctement le système graphique
And j’ai un utilisateur ‘1’ connecté
When l’ utilisateur saisi la valeur ‘1970’
Alors le résultat de Utilisateur[1].AnnéeDeNaissance est
‘1970’
Action/Verbe (step ré-utilisable)
Concept (ré-utilisable) ⇒ interface
IT’S ABOUT TDD (again)
A test written in Specflow can be re-
written in Nunit (or any test
framework)
A test written in Specflow form has the
same value than a Unit Test
Consider your Specflow tests as Unit
Tests
• It it executable
• It’s UNITaire
• It’s fast to execute
What are Tests ?
http://fr.slideshare.net/confiz/software-testing-methods-levels-and-types
Pourquoi les opposer?
Les tests dépendent de la vision du
logiciel que l’on a
et comment on le construit
Développeur = Testeur
Integration testing: Testing continuously evolving modules (with
defined expected actual outcome)
User acceptance testing: User representative testing (with defined
expected actual outcome)
Sanity testing: Testing after multiple builds (with defined expected
actual outcome)
Smoke testing: Testing after every build (with defined expected
actual outcome)
Exploratory testing: Unplanned and unscripted testing (no defined
expected actual outcome)
Regression testing: End-to-end testing done to ensure that
nothing is broken in the integrating modules -
https://www.scrumalliance.org/community/articles/2013/march/agile-methodology-is-not-all-about-
exploratory-tes#sthash.QEmYsyLg.dpuf
Yes, TDD has a cost!
• In 2008 Microsoft Research study and results published
in a paper Realizing quality improvement through test
driven development: results and experiences of four
industrial teams, by Nachi Nagappan and research
colleagues E. Michael Maximilien of IBM
• What the research team found was that the TDD teams
produced code that was 60 to 90 percent better in
terms of defect density than non-TDD teams.
• They also discovered that TDD teams took longer to
complete their projects—15 to 35 percent longer.
IT’S ABOUT DOCUMENTATION and
QUALITY
Gherkin / Cucumber / Specflow
fichiers textuels (.feature)
⇥ convertisseur html/pdf
⇥ liaison avec le repository de code (changes)
⇥ liaison avec le Product Backlog (UC / WI)
⇥ production de la documentation concernant
le release
⇥ mise au format du RQ/QA
⇥ signature.
REX
Documentation de 2000+ pages générée à la demande.
Générées automatiquement sans aucun effort ( ...
maintenant que les automatismes sont en place)
Personne de l’assurance qualité ne les lit, mais ils les
signent !
Les tests verts n’ont jamais tort!
Un bug = un test manque pour couvrir le cas rencontré.
Des développeurs essayent de se
rapprocher du fonctionnel
Et l’inverse ?
Specification by Exemple...
https://leanpub.com/livingdocumentation

Living Documentation (TDD, BDD).pptx

  • 1.
    Living Documentation Comment coderet transmettre ou @guillaume_agile
  • 2.
    Documenter? Pourquoi? Qui estle public visé? Qui va relire? Que voulons nous dire? Que voulons nous laisser comme trace?
  • 3.
  • 4.
    It is moreimportant to communicate to humans with your code than to communicate with a machine. Bob C. Martin
  • 5.
    Quels écueils ? ➔Avoir de la documentation non alignée et pas à jour ◆ dès qu’une ligne de code change, le comportement change et la documentation est obsolète ◆ dès qu’une ligne de spécification change, aussi ◆ on sent le besoin d’un lien entre les deux !!! ➔ Documentation qui n’a pas public ➔ Documentation illisible pour son public ➔ De la documentation inutile ◆ données que l’on a déjà ou qui se trouvent ailleurs (redondance) ◆ Informations inutiles (OSF)
  • 6.
    “La vérité estdans le code”
  • 7.
    Est ce bienle code qu’il faut documenter? Le code, s’il est bien écrit peut être très expressif: ➔ les noms sont importants: ◆ variables ◆ fonctions ◆ classes ➔ Sujets, verbes et compléments forment des phrases ➔ Les fonctions font ! ➔ Les fonctions sont fonctionnelles ➔ A l’intérieur du code, qui lit? qui va comprendre? qu’est ce qu’il y a comprendre? ➔ Essayez: fluent programming ➔ Essayez: functionnal programming ➔ Pensez à HATHEOAS
  • 8.
    DRY !!! ➔ Uniquementdes informations pertinentes ➔ Aucune redondance ➔ De l’information utile ➔ Qui donne du sens ➔ Qui soit compréhensible Le code c’est “COMMENT” Ce serait bien de documenter le POURQUOI ? !!! Relisez 3 fois le code pour savoir ce qu’il contient déjà... et ré-écrivez le!
  • 9.
    Redondant ? //ceci estune calculateur qui calcule public class Calculateur { //renvoit le résultat de a additioné à b public double Additionne (double a, double b ) { }
  • 10.
    Redondant ? //ceci estune calculateur qui calcule public class Calculateur { //renvoit le résultat de a additioné à b public double Additionne (double a, double b ) { return a - b; } // mais c’est pas grave ;)
  • 11.
    Compliqué ? Laid? static void Main() { try { // Iterate through all plug-ins. foreach (var filePath in Directory.GetFiles(Constants.PluginPath, Constants.PluginSearchPattern)) { // Create the plug-in AppDomain setup. var pluginAppDomainSetup = new AppDomainSetup(); pluginAppDomainSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; // Create the plug-in AppDomain with the setup. var plugInAppDomain = AppDomain.CreateDomain(filePath, null, pluginAppDomainSetup); // Pass the plug-in file path to the AppDomain var pluginContext = new PluginContext { FilePath = filePath }; plugInAppDomain.SetData(Constants.PluginContextKey, pluginContext); // Execute the loader in the plug-in AppDomain's context. // This will also execute the plug-in. plugInAppDomain.DoCallBack(PluginCallback); // Retrieve the flag if the plug-in has executed and can be deleted. pluginContext = plugInAppDomain.GetData(Constants.PluginContextKey) as PluginContext; // Unload the plug-in AppDomain. AppDomain.Unload(plugInAppDomain); // Delete the plug-in if applicable. if (pluginContext != null && pluginContext.CanDeletePlugin) { File.Delete(filePath); } } } catch (Exception exception) { Console.WriteLine(exception); } }
  • 12.
    public class JeFaisDesTrucsSuper{ public int JeLeFaisAvecUnUtilisateur() { //je regarde si je connais l'utilisateur if (e.AddedItems.Count > 0) { details.InitializeWith(e.AddedItems[0] as Model.Definition.AppointmentOrTask); MainGrid.ColumnDefinitions.Add(DetailsAreVisible); } else MainGrid.ColumnDefinitions.Add(DetailsAreInvisible); //je vais lui demander son année de naissance if (Sort == null) return false; Sort.IsAcsending = Sort.IsAcsending.HasValue && !Sort.IsAcsending.Value; var items = List.ItemsSource.Cast<object>(); List.ItemsSource = Sort.IsAcsending.Value ? items.OrderBy(o => SortBy.GetValue(o, null)) : items.OrderByDescending(o => SortBy.GetValue(o, null)); //je vais mémoriser son choix //je vais générer une chiffre au hasard //je vais demander l'age du captain //je vais additionner tout cela //et dire à l'utilisateur que j'ai trouvé son chiffre porte bonheur
  • 13.
    //je vais lemémoriser aussi using (var dbCommand = Sqlbase.CreateCommand (connection, defautCommandTimeOutForSelectStatement, selectCommand, CommandType.Text, spParams)) { Log.Write (LogLevel.Verbose, "Sql.Select: {0} with params: {1}", selectCommand.Replace ("rn", " "), DisplayParamsDetails (spParams)); IDbTransaction transaction = null; if (isolation != IsolationLevel.Unspecified) { transaction = connection.BeginTransaction (isolation); dbCommand.Transaction = transaction; } } //et je renvoie, on ne sait jamais return x; } } //fin de ma fonction
  • 14.
    Refactoring Single Responsability Nommage intelligibleet intelligent Qu’est ce qui est isolable et testable?
  • 15.
    public class JeFaisDesTrucsSuperMaisEnMieux { publicint JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui, IStockeDesTrucs stockage) { //je regarde si je connais l'utilisateur var esTuConnu = ui.Utilisateur.DemandeIdentite(); if (esTuConnu) return -1; //je vais lui demander son année de naissance var anneeNaissance = ui.QuelEstTonAnneeDeNaissance(); //je vais mémoriser son choix var resultatStockage = stockage.Sauvegarde( anneeNaissance, ui.Utilisateur.IdentifiantUniversel );
  • 16.
    public class JeFaisDesTrucsSuperMaisEnMieux { publicint JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui, IStockeDesTrucs stockage) { var esTuConnu = ui.Utilisateur.DemandeIdentite(); if (esTuConnu) return -1; var anneeNaissance = ui.QuelEstTonAnneeDeNaissance(); var resultatStockage = stockage.Sauvegarde( anneeNaissance, ui.Utilisateur.IdentifiantUniversel );
  • 17.
    Et quand onen a marre de lire le code.. Quelle serait la place pour une documentation efficace? Un document électronique qui n’est pas le code mais “entre” le code et les spécifications... Mais bon sang, c’est bien sûr....
  • 18.
  • 19.
    public class JeFaisDesTrucsSuperMaisEnMieux { publicint JeLeFaisAvecUnUtilisateur(IParleALUtilisateur ui, IStockeDesTrucs stockage) { var esTuConnu = ui.Utilisateur.DemandeIdentite(); if (esTuConnu) return -1; var anneeNaissance = ui.QuelEstTonAnneeDeNaissance(); var resultatStockage = stockage.Sauvegarde( anneeNaissance, ui.Utilisateur.IdentifiantUniversel );
  • 20.
    [TestFixture ()] public classTesteLeStockage { [Test ()] public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur () { //arrange int valeurAStocker = 101; int IdUser = 1; IJeStocke sut = depencyInjection.Get<IJeStocke> (); //act var resultatStockage = sut.Stocke (valeurAStocker, IdUser); sut.ResetConnection(); int verificationValeur = 0; var resultatLecture = sut.Lecture (out verificationValeur, IdUser); //assert Assert.That(verificationValeur , Is.EqualTo(valeurAStocker)); } }
  • 21.
    Les Tests Unitaires •Ils décrivent ce qui est attendu • Ils “déclenchent” le code • Ils sont organisés avec le code • Si le comportement du code change, au moins un de mes tests passe au rouge (sinon j’ai un souci...) • Donc ils vivent avec le code • Et si mes tests pouvaient être écrits dans un langage compréhensible par tous...
  • 22.
  • 23.
    [TestFixture ()] public classTesteLeStockage { [Test ()] public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur () { int valeurAStocker = 101; int IdUser = 1; IJeStocke sut = depencyInjection.Get<IJeStocke> (); var resultatStockage = sut.Stocke (valeurAStocker, IdUser); sut.ResetConnection(); int verificationValeur = 0; var resultatLecture = sut.Lecture (out verificationValeur, IdUser); Assert.That(verificationValeur , Is.EqualTo(valeurAStocker)); } }
  • 24.
    Behaviors Driven Tests Unesyntaxe simple et légère • Given • When • Then Des utilitaires pour faire tourner les tests basés sur *Unit * au choix: J/N/JS/x/.... Pas la peine d’en faire plus!
  • 25.
    Scenario: Le StockageDoit Memoriser Une Valeur Pour Un Utilisateur Given j’initialise correctement mon test And j’ai un utilisateur ‘1’ connecté When je sauve la valeur ‘101’ And je réinitialise le système And je lis la valeur stockée Alors le résultat de lecture est ‘101’
  • 26.
  • 27.
    Scenario: Le StockageDoit Memoriser Une Valeur Pour Un Autre Utilisateur Given j’initialise correctement mon test And j’ai un utilisateur ‘222’ connecté When je sauve la valeur ‘33333’ And je réinitialise le système And je lis la valeur stockée Alors le résultat de lecture est ‘3333’
  • 28.
    IT’S ABOUT BEINGMACHINE READABLE
  • 29.
    Executable Steps [Given(@"j’initialise correctementmon test")] public void GivenTestInit() { IJeStocke sut = depencyInjection.Get<IJeStocke> (); }
  • 30.
    Executable Steps (2) [Given(@"j’aiun utilisateur ‘(.*)’ connecté")] public void GivenTestInit(string x) { int IdUser = int.Parse( x ); }
  • 31.
    Regular expressions Pour l’instantla seule manière de passer du langage naturel à une expression comprise par la machine... Des idées pour le futur ?
  • 32.
    Executable Steps (2) [Given(@"j’aiun utilisateur ‘[([0-9]+)]’ connecté")] public void GivenTestInit(int x) { int IdUser = x ; }
  • 33.
    Step to Step [Given(@"j’aiun utilisateur ‘[([0-9]+)]’ connecté")] public void GivenTestInit(int x) { int IdUser = x ; ScenarioContext.Current.Set(IdUser, “IdUser”); }
  • 34.
    Step to Step [When(@"je sauve la valeur ‘‘[([0-9]+)]’’")] public void WhenValueIsSaved(int valeurAStocker) { var IdUser = ScenarioContext.Current.Get<int>( “IdUser”); var resultatStockage = sut.Stocke (valeurAStocker, IdUser); Assert.That (resultatStockage, Is.True); }
  • 35.
  • 36.
    Talking to objects Objects= instance de classe Class = abstraction, concept Interface = contrat, capacité de comportement
  • 37.
    Inter-active Steps [When(@" jesauve la valeur ‘‘[([0-9]+)]’’")] public void WhenValueIsSaved(int valeurAStocker) { var IdUser = ScenarioContext.Current.Get<int>( “IdUser”); var sut = ScenarioContext.Current.Get<IJeStocke> ( “CurrentTestStorage”); var resultatStockage = sut.Stocke (valeurAStocker, IdUser); Assert.That (resultatStockage, Is.True); }
  • 38.
    public interface IJeStocke { intStocke (int valeur, int identifiantUniversel); int Lecture (out int valeur, int identifiantUniversel); }
  • 39.
  • 40.
    [TestFixture ()] public classTesteLeStockage { [Test ()] public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur () { int valeurAStocker = 101; int IdUser = 1; IJeStocke sut = depencyInjection.Get<IJeStocke> (); var resultatStockage = sut.Stocke (valeurAStocker, IdUser); sut.ResetConnection(); int verificationValeur = 0; var resultatLecture = sut.Lecture (out verificationValeur, IdUser); Assert.That(verificationValeur , Is.EqualTo(valeurAStocker)); } }
  • 41.
    Scenario: LeStockageDoitMemoriserUneValeurPourUnUtilisateur Given j’initialise correctementmon test And j’ai un utilisateur ‘222’ connecté When je sauve la valeur ‘33333’ And je réinitialise le système And je lis la valeur stockée Alors le résultat de lecture est ‘3333’
  • 42.
    Interfaces : thehuman side of objects La documentation à travers les spécifications exécutables est le reflet des interfaces objets dans le monde des humains.
  • 43.
    public interface IJeStocke { intJeSauve (int valeur, int identifiantUniversel); int JeLis (out int valeur, int identifiantUniversel); }
  • 44.
    public interface IEntrepot { intJeSauve (int valeur, int identifiantUniversel); int JeLis (out int valeur, int identifiantUniversel); }
  • 45.
    public class NoSqlQuiPoutre: IEntrepot { public int JeSauve (int valeur, int identifiantUniversel) { ............;} public int JeLis (out int valeur, int identifiantUniversel) { ............;} } AI JE BESOIN DE COMMENTAIRES ???
  • 46.
    [TestFixture ()] public classTesteLeStockage { [Test ()] public void LeStockageDoitMemoriserUneValeurPourUnUtilisateur () { int valeurAStocker = 101; int IdUser = 1; IJeStocke sut = depencyInjection.Get<IJeStocke> (); var resultatStockage = sut.JeSauve (valeurAStocker, IdUser); sut.ResetConnection(); int verificationValeur = 0; var resultatLecture = sut.JeLis (out verificationValeur, IdUser); } } Change the way you code
  • 47.
    What are interfaces(really) ? Du rôle des interfaces Ainsi « l’interface devient le produit et l’objet du service ». Elle n’est plus un espace de commande utilisable, elle est la représentation du service, son usage et sa finalité symbolique, cognitive et esthétique. http://www.nodesign.net/blog/vers-un-confort-numerique-moderne/ Les Interfaces doivent être ergonomiques https://goo.gl/WUEh48
  • 48.
    BDD: It's aboutBehaviour BACKGROUND + GIVEN : Environnement + Setup WHEN: ACTION • Sujet • Verbe • Complement THEN: RESULTAT ATTENDU • Sujet • Etat END : Environnement rétalbi
  • 49.
    IT’S ABOUT BEINGMORE HUMAN READABLE
  • 50.
    Scenario: LeStockageDoitMemoriserUneValeurPourUnUtilisateur Given j’initialise correctementmon test And j’ai un utilisateur ‘1’ connecté When je sauve la valeur ‘101’ And je réinitialise le système And je lis la valeur stockée Alors le résultat de lecture est ‘101’
  • 51.
    Scenario: LeStockageDoitMemoriserUneValeurPourUnUtilisateur Given j’initialise correctementle système de stockage And j’ai un utilisateur ‘1’ connecté When je sauve la valeur ‘101’ pour cet utilisateur And je réinitialise le système de stockage And je lis la valeur stockée pour l’utilisateur connecté Alors le résultat de lecture est ‘1’
  • 52.
    Inter-active Steps [When(@" jesauve la valeur ‘‘[([0-9]+)]’’")] public void WhenValueIsSaved(int valeurAStocker) { var IdUser = ScenarioContext.Current.Get<IUtilisateur>( “User”); var sut = ScenarioContext.Current.Get<IJeStocke> ( “CurrentTestStorage”); var resultatStockage = sut.Stocke (valeurAStocker, IdUser); Assert.That (resultatStockage, Is.True); }
  • 53.
    IT’S ABOUT CODEBEING MORE HUMAN READABLE
  • 54.
    public interface IEntrepot { intJeSauve (int valeur, IUtilisateur utilisateur); int JeLis (out int valeur, IUtilisateur utilisateur); }
  • 55.
    public interface IUtilisateur { boolDemandeIdentite (ICredential credits); int DonneAnneeNaissance (); }
  • 56.
  • 57.
    Writing Test First Feature:identification avec des jetons Scenario: Un utilisateur est identifié Given j’initialise correctement mon test And j’ai un jeton égal à x10103 When je demande à identifier l’utilisateur avec ce jeton Alors le résultat est ok
  • 58.
    public class UtilisateurAJetons: IUtilisateur { #region IUtilisateur implementation bool IUtilisateur.DemandeIdentite (Icredential credits) { throw new NotImplementedException (); } int IUtilisateur.DonneAnneeNaissance () { throw new NotImplementedException (); } #endregion }
  • 59.
    Reusing Tests Feature: identificationavec Facebook Scenario: Un utilisateur est identifié Given j’initialise correctement mon test And j’ai un jeton égal à FB44103 When je demande à identifier l’utilisateur avec ce jeton Alors le résultat est ok
  • 60.
    public class UtilisateurFacebook: IUtilisateur { #region IUtilisateur implementation bool IUtilisateur.DemandeIdentite (Icredential credits) { throw new NotImplementedException (); } int IUtilisateur.DonneAnneeNaissance () { throw new NotImplementedException (); } #endregion }
  • 61.
  • 62.
    Est ce quemon test est lisible ? ⇥ Est ce que j’ai bien ré-utilisé mes steps ? ⇥ Est ce que j’ai généralisé les bons concepts ? ⇥ Design des interfaces et des objets
  • 63.
  • 64.
    Scenario: LeStockageDoitMemoriserUneValeurPourUnUtilisateur Given j’initialise correctementle système de stockage And j’ai un utilisateur ‘1’ connecté When je sauve la valeur ‘101’ pour cet utilisateur And je réinitialise le système de stockage And je lis la valeur stockée pour l’utilisateur connecté Alors le résultat de lecture est ‘1’
  • 65.
    Scenario: L’ Utilisateurdoit pouvoir saisir une année de naissance Given j’initialise correctement le système graphique And j’ai un utilisateur ‘1’ connecté When l’ utilisateur saisi la valeur ‘1970’ Alors le résultat de Utilisateur[1].AnnéeDeNaissance est ‘1970’
  • 66.
    Scenario: L’ Utilisateurdoit pouvoir saisir une année de naissance Given j’initialise correctement le système graphique And j’ai un utilisateur ‘1’ connecté When l’ utilisateur saisi la valeur ‘1970’ Alors le résultat de Utilisateur[1].AnnéeDeNaissance est ‘1970’ Action/Verbe (step ré-utilisable) Concept (ré-utilisable) ⇒ interface
  • 67.
  • 68.
    A test writtenin Specflow can be re- written in Nunit (or any test framework)
  • 69.
    A test writtenin Specflow form has the same value than a Unit Test
  • 70.
    Consider your Specflowtests as Unit Tests • It it executable • It’s UNITaire • It’s fast to execute
  • 71.
    What are Tests? http://fr.slideshare.net/confiz/software-testing-methods-levels-and-types
  • 72.
  • 73.
    Les tests dépendentde la vision du logiciel que l’on a et comment on le construit
  • 74.
    Développeur = Testeur Integrationtesting: Testing continuously evolving modules (with defined expected actual outcome) User acceptance testing: User representative testing (with defined expected actual outcome) Sanity testing: Testing after multiple builds (with defined expected actual outcome) Smoke testing: Testing after every build (with defined expected actual outcome) Exploratory testing: Unplanned and unscripted testing (no defined expected actual outcome) Regression testing: End-to-end testing done to ensure that nothing is broken in the integrating modules - https://www.scrumalliance.org/community/articles/2013/march/agile-methodology-is-not-all-about- exploratory-tes#sthash.QEmYsyLg.dpuf
  • 75.
    Yes, TDD hasa cost! • In 2008 Microsoft Research study and results published in a paper Realizing quality improvement through test driven development: results and experiences of four industrial teams, by Nachi Nagappan and research colleagues E. Michael Maximilien of IBM • What the research team found was that the TDD teams produced code that was 60 to 90 percent better in terms of defect density than non-TDD teams. • They also discovered that TDD teams took longer to complete their projects—15 to 35 percent longer.
  • 76.
  • 77.
    Gherkin / Cucumber/ Specflow fichiers textuels (.feature) ⇥ convertisseur html/pdf ⇥ liaison avec le repository de code (changes) ⇥ liaison avec le Product Backlog (UC / WI) ⇥ production de la documentation concernant le release ⇥ mise au format du RQ/QA ⇥ signature.
  • 78.
    REX Documentation de 2000+pages générée à la demande. Générées automatiquement sans aucun effort ( ... maintenant que les automatismes sont en place) Personne de l’assurance qualité ne les lit, mais ils les signent ! Les tests verts n’ont jamais tort! Un bug = un test manque pour couvrir le cas rencontré.
  • 79.
    Des développeurs essayentde se rapprocher du fonctionnel Et l’inverse ? Specification by Exemple...
  • 80.