4. It is more important 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)
7. 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
8. 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!
9. 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 )
{
}
10. 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 ;)
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 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
15. 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 );
16. 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 );
17. 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....
19. 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 );
20. [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));
}
}
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...
23. [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));
}
}
24. 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!
25. 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’
27. 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’
30. Executable Steps (2)
[Given(@"j’ai un utilisateur ‘(.*)’ connecté")]
public void GivenTestInit(string x)
{
int IdUser = int.Parse( x );
}
31. 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 ?
33. Step to Step
[Given(@"j’ai un 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);
}
36. Talking to objects
Objects = instance de classe
Class = abstraction, concept
Interface = contrat, capacité de comportement
37. 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);
}
38. public interface IJeStocke
{
int Stocke (int valeur, int identifiantUniversel);
int Lecture (out int valeur, int
identifiantUniversel);
}
42. 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.
43. public interface IJeStocke
{
int JeSauve (int valeur, int identifiantUniversel);
int JeLis (out int valeur, int identifiantUniversel);
}
44. public interface IEntrepot
{
int JeSauve (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 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
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
52. 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);
}
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: 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
60. public class UtilisateurFacebook : IUtilisateur
{
#region IUtilisateur implementation
bool IUtilisateur.DemandeIdentite (Icredential credits)
{
throw new NotImplementedException ();
}
int IUtilisateur.DonneAnneeNaissance ()
{
throw new NotImplementedException ();
}
#endregion
}
62. 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
65. 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’
66. 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
73. Les tests dépendent de la vision du
logiciel que l’on a
et comment on le construit
74. 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
75. 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.
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 essayent de se
rapprocher du fonctionnel
Et l’inverse ?
Specification by Exemple...