Friday, November 12, 2021

Règles sur le tests unitaires dans un projet x











------------ TEXTE

Bonne pratiques

Structure des Triple A

• Arrange, Act, Assert

• Les sections du test doivent être indiquées par un commentaire

[Fact]

public void ExempleStructureTripleA_AvecCommentaires()

{

    // Arrange


    // Act


    // Assert


}

Section Arrange

• Contient tout ce qui est nécessaire pour mettre en place les éléments nécessaires au test

o Création de l'élément de programme testé (SUT- ou System Under Test)

o Arguments

o Résultat attendu s'il y a lieu (pourrait aussi être fait dans la section Assert)

o Exécution d'autres méthodes ou fonction

Section Act

• Exécution de l'élément de programme testé

o Appel d'une méthode

o Exécution d'une fonction qui soulève une exception

o etc.

• En général une seule ligne de code

Section Assert

• Contient les assertions nécessaires à la vérification du comportement attendu

Quoi tester?

• En boîte noire, donc pas de test pour les méthodes privées, seulement les méthodes publiques

Comportement de l'élément de programme

• Comportement : Résultat produit par la fonctionnalité d'un élément de programme en fonction de certaines préconditions

o Préconditions : généralement l'intrant de l'élément de programme

o Résultat produit = Postconditions + effets de bord

Questions à se poser

• Quelle forme peut prendre l'intrant de l'élément de programme testé?

o Est-ce que toutes les valeurs permises ont été spécifiées?

• De quelle façon le succès ou l'échec est-il communiqué?

o Réaction aux intrants invalides

Recouvre?

Crash?

o Comportement inhabituel ou inattendu?

Assertions

Définition

• Ce sont des vérifications

• Est-ce que l'élément de programme se comporte tel qu'attendu?

o Est-ce que l'élément de programme fait ce qu'il est supposé faire?

o Est-ce que l'élément de programme ne fait PAS ce qu'il n'est pas

supposé faire?

• Vérifier le comportement

o Par la vérification du résultat produit, qui peut être

Une valeur de retour

Un changement à l'état du système (effet de bord)

Un appel à un contributeur

Quantité d'assertions nécessaire

• Idéalement : une seule assertion à la fin du test

• Les assertions multiples ajoutent de la complexité avec peu de valeur et rendent plus difficile l'identification du véritable problème

o Le test s'arrête dès la première assertion en échec

o Les assertions suivantes ne sont alors pas exécutées, se qui rend le diagnostique du problème incomplet

Quantité d'assertions nécessaire : Exceptions

Assertion de garde

• Une assertion de garde est une vérification de sécurité qui permet d'éviter d'avoir de la logique conditionnelle et protège des erreurs à l'exécution

• Exemples :

o Vérifier que le retour d'une méthode n'est pas null avant de faire d'autres assertions

o Vérifier le nombre d'éléments d'une collection avant d'accéder aux éléments

• Donc pas de if-else dans les tests!

Un concept sémantique qui n'est pas vérifiable par une seule assertion

• Par exemple, la vérification des éléments d'une collection pourrait nécessiter une assertion par élément

• Il existe des outils dans ce cas-ci

o FluentAssertions permet la vérification d'une collection avec une seule assertion, ainsi que CollectionAssert de MSTest

Noms des tests et gabarit

• Gherkin, style BDD

o ÉtantDonné_Lorsque_Alors

o Méthode_Contexte_RésultatAttendu

• Sous-classes de tests avec nom méthode testée

o Permet de renommer facilement si méthode renommée

o Organise les tests

public class SutTests

{

  public class UneMethode

  {

    [Fact]

    public void Contexte_RésultatAttendu()

    {

      \...

    }

  }

  public class UneAutreMethode

  {

    [Fact]

    public void Contexte_RésultatAttendu()

    {

      \...

    }

  }

}

Éléments à considérer

• Résistance au réusinage -\tenir au minimum l'utilisation des mocks

• Penser à la maintenabilité des tests : aussi important que le code de production

o Retour sur investissement vs coûts de maintenance des tests

o Cibler les parties les plus importantes de la base de code ->Domaine d'affaires

• Les mêmes règles de Clean Code / Bonnes pratiques s'appliquent ici!

o Code auto-documenté, clair, concis, expressif, etc.

o L'intention du test peut être comprise rapidement

Code ciblé

Domaine d'affaire

• Idéalement 100% de couverture de code

Infrastructure

• Repousser aux limites de la composante les I/O et effets de bord

• Couvrir les cas principaux (Happy path et Sad path)

Couverture de code

• Ne pas exclure de code de la couverture pour avoir la vérité en tout temps

Comportement vs interaction : Mocks

Pourquoi

• Pour isoler un élément de programme de ses dépendances sortantes

Quand

• Lorsque le contributeur est une dépendance sur laquelle nous n'avons pas le contrôle

o BD
o Service externe (SAGO, Moneris, etc) Système de fichier
o Librairie patrimoniale non
o etc.

Comment

• Principe : substituer la dépendance gênante pour un simulacre

o Si cette dépendance implémente une interface, doubler (mocker) cette interface

o Sinon, ajouter une couche d'abstraction supplémentaire devant cette dépendance, qui agira comme une sorte de proxy et qui deviendra la nouvelle dépendance de l'élément de programme testé

• Utiliser un framework d'isolation comme FakeItEasy

Bonnes pratiques

• Un seul mock par dépendance par test

• Une seule assertion sur le mock par test

o Vérifier soit le comportement, soit les interactions, mais pas les deux

• Éviter les new dans les objets gérés par injection de dépendances

• Repousser ces dépendances aux frontières du système (couche d'infrastructure) et garder le domaine d'affaires le plus pur possible

Tests paramétrés

• Ces tests sont pratiques pour spécifier par l'exemple

Exemple

private const string ouvertureSpan = "<span style=\"border-bottom: 4px solid #f19e8f; width: 6px">; 

private const string fermetureSpan = "</span>";


public static IEnumerable<object>ScenariosTest() => new[]

{

    new object[] { "", $"{ouvertureSpan}{fermetureSpan}" },

    new object[] { " un deux", $"{ouvertureSpan}{fermetureSpan} un deux" }, 

    new object[] { "t un deux", $"{ouvertureSpan}t{fermetureSpan} un deux" },

    new object[] { "te un deux", $"{ouvertureSpan}te{fermetureSpan} un deux" }, 

    new object[] { "test un deux", $"{ouvertureSpan}tes{fermetureSpan}t un deux" },

    new object[] { "testtest un deux", $"{ouvertureSpan}tes{fermetureSpan}ttest un deux" },

};


[Theory, MemberData(nameof(ScenariosTest))]

public void SelonTexteEnEntree_VerifierChaineHtmlAttendue(string texte, string chaineHtmlAttendue) 

{

    var htmlHelper = CreerHtmlHelper();

    var resultat = htmlHelper.SoulignerTroisPremieresLettres(texte);

    

    resultat.Should().Be(chaineHtmlAttendue);

}