Successfully reported this slideshow.
Your SlideShare is downloading. ×

Living Documentation (TDD, BDD).pptx

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 80 Ad
Advertisement

More Related Content

More from Guillaume Saint Etienne (15)

Recently uploaded (20)

Advertisement

Living Documentation (TDD, BDD).pptx

  1. 1. Living Documentation Comment coder et transmettre ou @guillaume_agile
  2. 2. Documenter? Pourquoi? Qui est le public visé? Qui va relire? Que voulons nous dire? Que voulons nous laisser comme trace?
  3. 3. Documenter? A quel prix?
  4. 4. It is more important to communicate to humans with your code than to communicate with a machine. Bob C. Martin
  5. 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. 6. “La vérité est dans le code”
  7. 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. 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. 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. 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. 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. 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. 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
  14. 14. Refactoring Single Responsability Nommage intelligible et intelligent Qu’est ce qui est isolable et testable?
  15. 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. 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. 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....
  18. 18. NOS AMIS LES TESTS
  19. 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. 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. 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. 22. IT’S ABOUT TELLING A STORY
  23. 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. 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. 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’
  26. 26. IT’S ABOUT BEING FORMAL
  27. 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’
  28. 28. IT’S ABOUT BEING MACHINE READABLE
  29. 29. Executable Steps [Given(@"j’initialise correctement mon test")] public void GivenTestInit() { IJeStocke sut = depencyInjection.Get<IJeStocke> (); }
  30. 30. Executable Steps (2) [Given(@"j’ai un utilisateur ‘(.*)’ connecté")] public void GivenTestInit(string x) { int IdUser = int.Parse( x ); }
  31. 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 ?
  32. 32. Executable Steps (2) [Given(@"j’ai un utilisateur ‘[([0-9]+)]’ connecté")] public void GivenTestInit(int x) { int IdUser = x ; }
  33. 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. 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. 35. IT’S ABOUT OBJECTS
  36. 36. Talking to objects Objects = instance de classe Class = abstraction, concept Interface = contrat, capacité de comportement
  37. 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. 38. public interface IJeStocke { int Stocke (int valeur, int identifiantUniversel); int Lecture (out int valeur, int identifiantUniversel); }
  39. 39. IT’S ABOUT BEING HUMAN
  40. 40. [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)); } }
  41. 41. 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’
  42. 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. 43. public interface IJeStocke { int JeSauve (int valeur, int identifiantUniversel); int JeLis (out int valeur, int identifiantUniversel); }
  44. 44. public interface IEntrepot { int JeSauve (int valeur, int identifiantUniversel); int JeLis (out int valeur, int identifiantUniversel); }
  45. 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. 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. 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. 48. BDD: It's about Behaviour BACKGROUND + GIVEN : Environnement + Setup WHEN: ACTION • Sujet • Verbe • Complement THEN: RESULTAT ATTENDU • Sujet • Etat END : Environnement rétalbi
  49. 49. IT’S ABOUT BEING MORE HUMAN READABLE
  50. 50. 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’
  51. 51. 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’
  52. 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); }
  53. 53. IT’S ABOUT CODE BEING MORE HUMAN READABLE
  54. 54. public interface IEntrepot { int JeSauve (int valeur, IUtilisateur utilisateur); int JeLis (out int valeur, IUtilisateur utilisateur); }
  55. 55. public interface IUtilisateur { bool DemandeIdentite (ICredential credits); int DonneAnneeNaissance (); }
  56. 56. IT’S ABOUT TDD
  57. 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. 58. public class UtilisateurAJetons : IUtilisateur { #region IUtilisateur implementation bool IUtilisateur.DemandeIdentite (Icredential credits) { throw new NotImplementedException (); } int IUtilisateur.DonneAnneeNaissance () { throw new NotImplementedException (); } #endregion }
  59. 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. 60. public class UtilisateurFacebook : IUtilisateur { #region IUtilisateur implementation bool IUtilisateur.DemandeIdentite (Icredential credits) { throw new NotImplementedException (); } int IUtilisateur.DonneAnneeNaissance () { throw new NotImplementedException (); } #endregion }
  61. 61. IT’S ABOUT REFACTORING
  62. 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
  63. 63. IT’S ABOUT REUSING
  64. 64. 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’
  65. 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. 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
  67. 67. IT’S ABOUT TDD (again)
  68. 68. A test written in Specflow can be re- written in Nunit (or any test framework)
  69. 69. A test written in Specflow form has the same value than a Unit Test
  70. 70. Consider your Specflow tests as Unit Tests • It it executable • It’s UNITaire • It’s fast to execute
  71. 71. What are Tests ? http://fr.slideshare.net/confiz/software-testing-methods-levels-and-types
  72. 72. Pourquoi les opposer?
  73. 73. Les tests dépendent de la vision du logiciel que l’on a et comment on le construit
  74. 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. 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.
  76. 76. IT’S ABOUT DOCUMENTATION and QUALITY
  77. 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. 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. 79. Des développeurs essayent de se rapprocher du fonctionnel Et l’inverse ? Specification by Exemple...
  80. 80. https://leanpub.com/livingdocumentation

×