Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

La qualité logicielle

  • Des délais de livraison non tenus
  • Des dépassements de 50% à 70% des coûts prévisionnels
  • Des anomalies techniques
  • Des décalages avec les besoins du client
  • Des difficultés à réaliser des évolutions
  • Des difficultés à réutiliser le logiciel dans d'autres contextes de développement
  • ...

Qu'est-ce que c'est ?

Qualité du produit Fiabilité
Capacité fonctionnelle
Performances
Maintenabilité
Facilité d'utilisation
Portabilité
Qualité des processus Respect des coûts et délais
Evaluation de la qualité
Temps de montée en compétence
Exploitabilité
Fiabilisation des livraisons
Gestion des versions et correctifs

Les idées reçues

  • Tester coûte cher !
  • Tester est une perte de temps !
  • Si ça ne marche pas, soit c'est le client qui s'est mal exprimé, soit c'est l'utilisateur qui ne sait pas se servir du logiciel !
  • Une fois livré, les problèmes ne sont plus de ma responsabilité...
  • On ne peut tester un logiciel que lorsqu'il est codé !
  • Ce sont les testeurs qui doivent tester !
  • Les tests doivent être automatiques !
  • Plus il y a de tests mieux c'est !
  • Tester permet de trouver tous les bugs !
  • Si tous mes tests passent, mon logiciel est de qualité !

Les outils et pratiques indispensables

Outils

  • Gestionnaire de code source
  • Plateforme d'intégration continue
  • Plateforme d'évaluation de la qualité
  • Outils de plannification des itérations
  • Plateforme de gestion des anomalies

Pratiques

  • Pair programming
  • Revues de code
  • Rétrospectives
  • Implication des développeurs dans les chiffrages
  • Implication du client

La gestion de projet

Le développement itératif

Crédit image Safety Dave

Exemple avec Scrum

Les pré-requis

  • Une équipe stable, multi-niveaux et multi-compétences d'entre 6 et 10 développeurs
  • Un cycle de développement entre 2 et 4 semaines
  • Des itérations figées une fois débutées
  • Une équipe impliquée dans le chiffrage des tâches
  • Du temps accordé aux cérémonies

Le Product Owner

Le Scrum Master

  • Le Scrum Master n'est pas
  • Le chef
  • Le décideur
  • Le manager
  • Un intermédiaire
  • Le Scrum Master est
  • Un facilitateur
  • Un protecteur
  • Le gourou de l'agilité
  • Au service de l'équipe

Planning de sprint

Definition Of Ready

Le sprint

Le daily meeting

Definition Of Done

Revues de code

Revue de sprint

Rétrospective

Fiabilité

  • Intégration continue
  • Evaluation de la qualité
  • Tests unitaires
  • Tests d'intégration
  • Métriques

Intégration continue

Évaluation de la qualité

Les tests unitaires

  • Un bon test unitaire doit être...
  • unitaire
  • indépendant
  • simple
  • reproductible
  • rapide
  • clair

JUnit

  • Classe et méthodes de test
  • Vérifications
  • Tests paramétrés
  • Ressources de test
  • Tests avec Spring
  • Initialisation et Réinitialisation
  • Mock

Classe et méthodes de test

public class VehiculeTest {

  @Test //Junit 4 : org.junit.Test, Junit 5 : org.junit.jupiter.api.Test
  public void testCheckBadImmatriculation(){
      //Given = Initialisation des données d'entrée
      String immatriculation = "XXXXXX";

      //When = Exécution de la méthode à tester
      Boolean immatOk = Vehicule.checkImmatriculation(immatriculation);

      //Then = Vérifications de ce qu'a fait la méthode
      Assertions.assertThat(immatOk).isFalse();
}

Vérifications

public class VehiculeTest {
  @Test
  public void testCheckBadImmatriculation(){
    //Given, When
    //Then (org.assertj.core.api.Assertions)
    Assertions.assertThat(booleen).isFalse(); //isTrue
    Assertions.assertThat(booleen).isNull(); //isNotNull
    Assertions.assertThat(o).isEqualTo(o2); //isNotEqualTo
    Assertions.assertThat(s).contains("t"); //doesNotContain
    Assertions.assertThat(s).startsWith("t"); //endWith
    Assertions.assertThat(s).isEmpty(); //isNotEmpty
    Assertions.assertThat(n).isPositive(); //isNegative
    Assertions.assertThat(n).isGreaterThan(5); //isLessThan
    Assertions.assertThat(n).isGreaterThanOrEqualTo(5);
    Assertions.assertThat(list).hasSize(3);
    ...
}

public class VehiculeTest {
  @Test
  public void testCheckBadImmatriculation(){
    //Given, When
    //Then org.junit.jupiter.api.Assertions;
    Assertions.assertFalse(booleen); //assertTrue
    Assertions.assertNull(booleen); //assertNotNull
    Assertions.assertEquals(expected, actual); //assertNotEquals
    Assertions.assertSame(expected, actual); //assertNotSame
    Assertions.assertArrayEquals(expected, actual);
    ...
}

Tests paramétrés (Junit 4)

@RunWith(value = Parameterized.class)//org.junit.runners.Parameterized
public class VehiculeTest {
  @Parameter(value = 0)//org.junit.runners.Parameterized.Parameter
  public String immat;
  @Parameter(value = 1)
  public Boolean result;
  //org.junit.runners.Parameterized.Parameters
  @Parameters(name = "immat {0} est valide : {1}")
  public static Collection<Object[]> data() {
      return Arrays.asList(new Object[][]{
        {"XXXXX", false}, {"AA-123-BB", true}
      });
  }
  @Test
  public void testCheckBadImmatriculation(){
      //Given, When, Then
      Assertions.assertThat(Vehicule.checkImmat(immat)).isEqualTo(result);
  }

Tests paramétrés (Junit 5)

@ParameterizedTest(name = "immat {0} est valide : {1}")
@CsvSource({
        "'XXXXX', false",
        "'AA-123-BB', true"
})
void testCheckBadImmatriculation(String immat, Boolean result) {
    //Given, When, Then
    Assertions.assertThat(Vehicule.checkImmat(immat)).isEqualTo(result);
}

Ressources de test

//src/test/resources/application.properties

spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa

Tests avec Spring

@RunWith(SpringRunner.class) //Junit 4
@ExtendWith(SpringExtension.class) //Junit 5 
@DataJpaTest // ou @SpringBootTest
public class VehiculeRepositoryTest {

    @Autowired
    VehiculeRepository vehiculeRepository;
    @Test
    public void testFindByImmat(){
        //Given
        Vehicule v = vehiculeRepository.save(new Vehicule("AA-123-BB"));
        //When
        Vehicule result = vehiculeRepository.findByImmat("AA-123-BB");
        //Then
        Assertions.assertThat(result).isEqualTo(v); 
    }
}

Initialisation et Réinitialisation

public class ExempleTest {
  @BeforeClass //Junit 4
  public void setUp(){//Nom setUp arbitraire
    //Appelé une seule fois avant l'exécution des tests
  }
  @Before //Junit 4
  public void before(){//Nom before arbitraire
    //Appelé avant chaque test
  }
  @After //Junit 4
  public void after(){//Nom after arbitraire
    //Appelé après chaque test
  }
  @AfterClass // Junit 4
  public void tearDown(){//Nom tearDown arbitraire
    //Appelé une fois que tous les tests sont passés
  }
}
public class ExempleTest {
  @BeforeAll // Junit 5
  public void setUp(){//Nom setUp arbitraire
    //Appelé une seule fois avant l'exécution des tests
  }
  @BeforeEach // Junit 5
  public void before(){//Nom before arbitraire
    //Appelé avant chaque test
  }
  @AfterEach //Junit 5
  public void after(){//Nom after arbitraire
    //Appelé après chaque test
  }
  @AfterAll // Junit 5
  public void tearDown(){//Nom tearDown arbitraire
    //Appelé une fois que tous les tests sont passés
  }
}

Mock

//Junit 4
@RunWith(MockitoJUnitRunner.class) 
public class VehiculeServiceTest {

  @InjectMocks
  private VehiculeService vehiculeService;
  @Mock
  private VehiculeRepository vehiculeRep;

  @Test(expected = EntityNotFoundException.class)
  public void testFindByImmatNotFound(){
    //Given
    Mockito.when(vehiculeRep.findByImmat("X")).
     thenReturn(null);
    //When Junit 4
    vehiculeService.findByImmat("X");
    //Then Exception
  }
}
//Junit 5
@ExtendWith(MockitoExtension.class) 
public class VehiculeServiceTest {
  //...
  @Test
  public void testFindByImmatNotFound(){
    //Given
    Mockito.when(vehiculeRep.findByImmat("X")).thenReturn(null);
    //When Junit 5
    EntityNotFoundException e = 
    Assertions.assertThrows(EntityNotFoundException.class, () -> 
     vehiculeService.findByImmat("X")
    );
    //Then Exception
    Assertions.assertEquals(e.getMessage(), "Véhicule X non trouvé!");
  }
}

Mock (2)

public class VehiculeServiceTest {
    @InjectMocks
    public VehiculeService vehiculeService;
    @Mock
    public VehiculeRepository vehiculeRepository;
    @Test
    public void testAddProprietaire(){
        //Given
        Vehicule vehicule = new Vehicule("AA-123-BB");
        Mockito.when(vehiculeRepository.findOne(Mockito.anyLong())).thenReturn(vehicule);
        Mockito.when(vehiculeRepository.save(Mockito.any(Vehicule.class))).thenAnswer(AdditionalAnswers.returnsFirstArg());
        //When addProprietaire(idVehicule, idProprietaire)
        vehiculeService.addProprietaire(1L, 2L);
        //Then
        ArgumentCaptor<Vehicule> vehiculeCaptor = ArgumentCaptor.forClass(Vehicule.class);
        Mockito.verify(vehiculeRepository, Mockito.times(1)).save(vehiculeCaptor.capture());
        Assertions.assertThat(vehiculeCaptor.getValue().getProprietaireId()).isEqualTo(2L);
    }
}

Les tests d'intégration

  • Un bon test d'intégration doit être...
  • indépendant
  • rapide
  • pertinent

Les tests d'intégration avec JUnit

@RunWith(SpringRunner.class) // Junit 4
@ExtendWith(SpringExtension.class) // Junit 5
@SpringBootTest
public class VehiculeServiceTest {

    @Autowired
    public VehiculeService vehiculeService;

    @Test
    public void test(){
        //Given avec de vraies données d'entrées

        //When avec appel des vraies méthodes de repository...
        vehiculeService.test();

        //Then avec de vraies vérifications...
    }
}

Métriques

  • Résultats des tests unitaires et d'intégration (% de tests OK)
  • Couverture de code (%)
  • Couverture de code avec mutation (%)
  • Complexité (application, classe, méthode)
  • Lignes de code en commentaire

Capacité fonctionnelle

  • Tests d'acceptation
  • Tests d'interface
  • Tests manuels
  • Métriques

Les tests d'acceptation

  • Un bon test d'acceptation doit être...
  • non technique
  • uniquement fonctionnel
  • non équivoque
  • écrit par le client
  • écrit avant le développement
  • automatisé

Les tests d'interface



  • Un bon test d'interface doit être...
  • automatisé
  • déployé sur plusieurs plateformes
  • indispensable
  • ... Y a-t-il vraiment de bons tests d'interface ?

Les tests manuels

  • Un bon test manuel doit être...
  • automatisé
  • scénarisé
  • pas trop long...

Métriques

  • Résultats des tests d'acceptation (% de tests OK)
  • Résultats des tests d'interface (% de tests OK)
  • Résultats des tests manuels (% de tests OK)
  • Satisfaction client (largeur de sourire ?)

Performances

  • Tests de charge
  • Métriques

Les tests de charge

  • Un bon test de charge doit être...
  • pertinent
  • surveillé
  • complet
  • réaliste

Métriques

  • Web : Temps de chargement des pages (durée)
  • Web : Temps des recherches (durée)
  • Batch : Nombre de lectures/écritures (nombre par seconde)
  • Application : Temps de chargement
  • Temps d'exécution des tests
  • Evolution des tests de charge

Maintenabilité

  • Journalisation
  • Documentation
  • Développements spécifiques
  • Livraisons
  • Métriques

Journalisation

  • Actions utilisateur (CRUD)
  • Compte-rendu de traitements automatiques
  • Informations sur le client (OS, navigateur...)
  • Seuils techniques ou fonctionnels atteints
  • Erreurs techniques ou fonctionnelles

Documentation

/*
 *  Première classe d'exemple
 *  pour le cours de test
 */
public class HelloWorld
{
    /**
     * Point d'entrée du programme
     * 
     * @param args Tableau des arguments passés au programme
    */
    public static void main(String[] args)
    {
        //Affiche Hello World dans la console
        System.out.println("Hello World");
    }
}

Documentation projet

Développements spécifiques

  • Interface de monitoring de traitements
  • Pages web d'administration (CRUD)
  • Suivi des interventions
  • Suivi des montées de version
  • Automatisation de tâches d'exploitation
  • ...

Livraisons

  • Un bon processus de livraison doit être...
  • automatisé
  • tolérant aux pannes
  • transparent
  • réversible
  • traçable
  • documenté

Métriques

  • Nombre de lignes de commentaires et % par rapport au code
  • Code smells (nombre)
  • Dette technique (durée)
  • Code dupliqué (% et nombre de lignes)

Conclusion

Source : https://intland.com/devops-it-operations/