Les tests : pourquoi et comment ?

Axel Haustant, Xavier Renaudin et Grégory Paul

Les tests : pourquoi et comment ?

14h30 - 15h20 - Salle La Seine C

Présentation

Axel

Développeur Python,
Équipe Data

Xavier

Analyste fonctionnel,
Équipe PO

Grégory

Développeur JavaScript,
Équipe Web

Mappy, c’est quoi ?

Sommaire

Introduction

On l’a tous entendu

Idées reçues

Je perds du temps à réfléchir comment tester alors que j’ai déjà écrit mon code…
Nos tests sont rouges depuis trop longtemps, du coup on ne les regarde plus…
Les développeurs disent tout tester et pourtant on trouve encore des bugs…
Le fonctionnel/IHM/Architecture du projet est trop compliqué pour pouvoir être testé…
Je ne pourrais tester que quand l’application sera terminée…

Contexte chez Mappy

Une année de:

  • Changement de méthodologie
  • Fusion de 2 plateformes différentes
  • Mise en intégration continue
  • Nettoyage

Pourquoi ?

Définir

Pourquoi ?

  • Spécifie efficacement le produit
    • Toujours à jour (≠ Documents de spécification)
    • Lisible (≠ code)
  • "Definition Of Done"
    • Vert = Ce que le produit sait faire
    • Rouge = Ce que le produit devrait faire

Valider

Pourquoi ?

  • Valide le "Done"
    • Vert = Done. On peut passer (penser) à autre chose
  • Feedback sur la "Situation Of Done"
    • Rouge = Work In Progress. On sait exactement ce qu'il reste à faire

Détecter

Pourquoi ?

  • Non-regression du produit
    • Rouge = Quelque chose a fait casser le produit
  • Amélioration continue
    • Nouveau bug ⇨ Nouveau test

Comment ?

Implication

Ligne directrice

Comment ?Implication

  • Stratégie pour vos tests
  • Vision partagée
  • Parlez-en

Motivation / Envie

Comment ?Implication

  • Code review, Coding dojo
  • Pair programming, TDD / ATDD
  • Faites en un jeu

No Broken window / Stop the line

Comment ?Implication

  • Une stratégie face au build rouge
  • Pas de fonctionalités tant que le build est rouge
  • Binôme de maintenance responsable du build (et des tests)

Expérience

Savoir Prioriser

Comment ?Expérience

Priorisation verticale

  • Pyramide de Cohn

Priorisation horizontale

  • Chemin critique

Savoir Explorer

Comment ?Expérience

  • Sortir du chemin critique
  • Anticiper
  • Remise en question

Savoir éviter les redondances

Comment ?Expérience

  • Savoir éviter de dire plusieurs fois la même chose
  • Est-ce que c'est vraiment pareil ?

Visibilité

Intégration continue

Comment ?Visibilité

  • Tester en continu et dès le début du projet
  • Votre produit n'est pas seul
  • Tout le monde doit savoir que le produit fonctionne (ou pas)

Accessible pour tous

Comment ?Visibilité

  • Tests doivent être visibles par tout le monde
  • Rapport de tests
  • Notification systématique

Partage

Comment ?Visibilité

  • Tests écrits par l’équipe et le PO
  • Affichez vos métriques
  • Parlez-en entre vous

Démo FitNesse

Lisibilité

Given / When / Then

Comment ?Lisibilité

                // Given
                User user = new User();
                user.setPlainTextPassword("password");
                 
                // When
                String hashedPassword = user.getHashedPassword(salt);
                 
                // Then
                assertThat(hashedPassword).isEqualTo("4ccc916...");
            

Documentation par le code

Comment ?Lisibilité

                TestBadFormat1()
            
                given_file_with_no_extension_import_should_fail()
            
  • Intention de test (via le nom de méthode)
  • Evitez les termes trop génériques

Soyez explicite

Comment ?Lisibilité

                public void should_drop_line_when_poi_with_no_name_is_read() {
                    when(matcherStepMock.run(any(FullGeoentity.class)))
                        .thenThrow(new RuntimeException("I should not be called"));
                    createInjector(...).getInstance(Launcher.class).run();
                }
            
                public void should_drop_line_when_poi_with_no_name_is_read() throws Exception {
                    // Given
                    reader.open(PoiReaderTest.class.getResourceAsStream("/invalid_poi.csv"));
                    try { // When
                       reader.readLine();
                    } catch(DroppingEntityException e) {
                      // Then
                    }
                    fail("readline should have thrown DroppingEntityException");
                }
            

Créez vos assertions

Comment ?Lisibilité

Factorisez les validations communes :

                assertThat(xml).contains("<root>"));
                assertThat(xml).contains("<child>");
                assertThat(xml).contains("</child>");
                assertThat(xml).contains("</root>");
            
                assertThat(xml).isWellFormed();
            

Facilité d’écriture

Factorisation

Comment ?Facilité d’écriture

  • Passer moins de temps à écrire les tests
  • Utiliser les outils du framework de test
  • N'allez pas trop loin !

Factorisation des données

Comment ?Facilité d’écriture

  • Fixtures
  • Factories

Factorisation du code

Comment ?Facilité d’écriture

  • Générateur
  • Mixins
  • Paramétrage

Paramétrage

Comment ?Facilité d’écriture

                @RunWith(Parameterized.class)
                public class NumberTest {
                    private Integer number;
                 
                    public MyUnitTest(Integer number) {
                        this.number = number;
                    }
                 
                    @Parameters
                    public static Collection numbers() {
                        return Arrays.asList(1, 2, 3);
                    }
                 
                    public void should_be_less_than_3() {
                        assertThat(this.number).isLessThan(4);
                    }
                }
            

Choissisez des technos reconnues

Comment ?Facilité d’écriture

Quand on a un marteau en main, tout ressemble à un clou

Valider des interfaces Web

Comment ?Facilité d’écriture

CasperJS Étapes de tests, remplissage de formulaire, capture d’écrans, évaluation du JavaScript du client, téléchargements, événements : onResourceRequested, onResourceReceived, onLoadError,...

Démo CasperJS

Rapidité d’exécution

Execution conditionnelle

Comment ?Rapidité d’exécution

Timeouts pour éviter les tests qui ne finissent pas

                @Test(timeout=1000)
            

Ignorer certains tests en fonction des resources

                @Test
                public void post_a_comment() {
                    Assume.assumeTrue(canWriteInThatEnvironment());
                }
            

Empreinte minimale

Comment ?Rapidité d’exécution

  • @Before/@After vs @BeforeClass/@AfterClass
  • Ne chargez pas tout à chaque test
  • Fixtures vs Factories
  • Mocks et stubs
  • Attention à l'injection de dépendances

Paralléliser

Comment ?Rapidité d’exécution

                mvn -T 4 test
                ls suites/ | parallel "casperjs test suites/{}"
            

Empêchez les de dormir

Comment ?Rapidité d’exécution

                driver.findElement(By.name("q")).sendKeys("devoxx");
                Thread.sleep(1000);
            
                driver.findElement(By.name("q")).sendKeys("devoxx");
                (new WebDriverWait(driver, 10)).until(
                  new ExpectedCondition() {
                    public Boolean apply(WebDriver d) {
                        return d.findElements(By.cssSelector(...);
                  }});
            

Analyse

Un besoin par test

Comment ?Analyse

1 besoin/test ≠ 1 assertion/test ≠ n besoins/test

                public void should_cleanup_kitchen() {
                    assertThat(makeCoffee()).isHot().isTasty();
                    assertThat(washTheDishes()).isDone().isClean();
                }
            
                public void should_make_coffee() {
                    assertThat(makeCoffee()).isHot().isTasty();
                }
                public void should_wash_the_dishes() {
                    assertThat(washTheDishes()).isDone().isClean();
                }
            

Découper l’exécution

Comment ?Analyse

  • Mieux vaut plusieurs petit test qu'un gros test
  • Mieux découper = Mieux détecter = feedback plus rapide
                mvn test -Dtest=MyTestClass#myTestMethod
                mvn test -Dtest=MyTestClass
            

Utilisez les bonnes assertions

Comment ?Analyse

                assertTrue("bar".contains("foo"));
                java.lang.AssertionError:
                    at org.junit.Assert.fail(Assert.java:91)
            
                assertThat("foo").contains("bar");
                java.lang.AssertionError: 
                Expected: a string containing "bar"
                     but: was "foo"
            

Commentez vos assertions

Comment ?Analyse

                assertEquals(200, status)
                java.lang.AssertionError: 
                    expected:<200> but was:<301>
            
                assertEquals("La page d’accueil ne répond pas", 
                  200, status);
                java.lang.AssertionError: 
                    La page d’accueil ne répond pas
                    expected:<200> but was:<301>
            

Amélioration

Détection en continue

Comment ?Amélioration

Corruption de données ? Le service répond ?

Les tests doivent évoluer

Comment ?Amélioration

  • Bonne couverture ≠ couverture du code à 100%
  • Un bug = un nouveau test
  • Détection comportements utilisateurs pour enrichir tests

Refactoring

Comment ?Amélioration

  • Refactoring du code à périmètre fonctionnel identique : surtout ne pas modifier les tests !
  • Changement fonctionnel : modifier les tests avant (cible)
  • Les deux en même temps : problèmes !!!

Comprendre l'origine d'un bug

Comment ?Amélioration

Un test sera très efficace pour retrouver un bug déjà existant.

Demo Git Bisect

Conclusion

Conclusion

  • Soignez vos tests, c’est un bon investissement
  • Utilisez et rentabilisez les outils
  • Communiquez plus
  • N’en faîtes pas trop

Cette présentation et ses démos sont disponibles sur : http://valtechtechno.github.com/devoxx-2013-tests-pourquoi-comment/ (slides) https://github.com/ValtechTechno/devoxx-2013-tests-pourquoi-comment (sources)

Questions ?

Références