Fractionnement des tests d'unités par exigence ou par méthode

Tout d'abord, excuses pour le titre, je ne pouvais pas penser à la meilleure façon de l'expliquer!

J'ai une méthode pour laquelle je veux écrire des tests unitaires. Je vais la garder assez générique car je ne veux pas discuter de la mise en œuvre de la méthode, mais simplement de la tester. La méthode est la suivante:

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

Donc, cette classe a une méthode publique qui appelle ensuite certaines méthodes privées pour exécuter la logique.

Donc, lors de l'écriture du test unitaire, je veux vérifier que les trois choses ont été faites. Comme ils sont tous appelés dans le même cycle, j'ai pensé que je pouvais le faire en un seul test:

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
    //Assert item has been created
    //Assert status has been set on the previous item
    //Assert run date has been set
}

Mais je pensais pouvoir aussi l'écrire sous forme de trois tests distincts:

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

Donc, pour moi, cela semble plus sympa car il s'agit essentiellement d'une liste d'exigences, mais les trois sont liées et exigent le même travail effectué sur la méthode testée. Le même code est exécuté trois fois.

Y at-il une approche recommandée à prendre avec cela?

Merci

16
ajouté
Vues: 1

6 Réponses

Il existe une différence subtile entre les deux approches. Dans le premier cas, lorsque le premier Assert échoue, les deux autres ne sont plus exécutés. Dans le second cas, les trois tests sont toujours exécutés, même si un échoue. Selon la nature de la fonctionnalité testée, cela pourrait s’intégrer ou non à votre cas:

  • s'il est logique d'exécuter les trois assertions indépendamment les unes des autres, car lorsqu'une échoue, les deux autres peuvent ne pas échouer, la seconde approche a l'avantage de vous permettre d'obtenir les résultats complets des 3 tests en une course. Cela peut être bénéfique si vous avez des temps de construction notables, car cela vous donne une chance de corriger 3 erreurs en même temps avant de faire la prochaine construction.

  • si, toutefois, un échec du premier test implique toujours que les deux autres tests échouent également, il est probablement préférable d'utiliser la première approche (car cela n'a pas beaucoup de sens de faire un test si vous savez déjà qu'il échouera).

29
ajouté
@KilianFoth: Vous ne travaillez pas assez souvent en C ++ :(
ajouté l'auteur Owen, source
@MatthieuM .: pour être juste, la question est étiquetée "C #"
ajouté l'auteur Peter LeFanu Lumsdaine, source
+1, bon point. Je ne me suis pas rendu compte que les périodes de construction peuvent également constituer un goulot d'étranglement.
ajouté l'auteur sigirisetti, source

Réponse courte: Il est beaucoup plus important que vos tests couvrent toutes les fonctionnalités que comment ils le font.

Réponse plus longue: Si vous souhaitez toujours choisir parmi ces solutions largement équivalentes, vous pouvez utiliser des critères auxiliaires pour déterminer ce qui convient le mieux. Par exemple,

  • lisibilité: si la méthode fait beaucoup de choses qui ne sont pas étroitement liées, un test combiné peut être difficile à comprendre. Cependant, la méthode elle-même peut aussi être difficile à comprendre, alors vous devriez peut-être la refactoriser plutôt que le test!)
  • efficacité: si l'exécution de la méthode prend beaucoup de temps, ce n'est peut-être pas une raison suffisante pour combiner les trois vérifications afin de gagner du temps
  • efficience2: si l'exécution du code d'installation de votre infrastructure prend beaucoup de temps, cela pourrait également constituer une faible raison d'éviter de recourir à plusieurs méthodes de test. (Cependant, si cela pose vraiment un problème, vous devriez probablement corriger ou modifier votre configuration de test - les tests de régression perdent une grande partie de leur valeur si vous ne pouvez pas les exécuter à la vitesse de l'éclair.)
11
ajouté

Utilisez un appel de méthode avec plusieurs assertions. Voici pourquoi:

Lorsque vous testez HandleItem (a), vous vérifiez que la méthode a amené l'élément à l'état correct. Au lieu de "une assertion par test", pensez à "un concept logique par test".

Question: Si un CreateNewItem échoue, mais que les deux autres méthodes réussissent, cela signifie-t-il que HandleItem a abouti? Je suppose que non.

Avec plusieurs assertions (avec les messages appropriés), vous saurez exactement ce qui a échoué. Vous testez généralement une méthode plusieurs fois pour plusieurs entrées ou états d'entrée, afin d'éviter plusieurs assertions.

OMI, ces questions sont généralement le signe de quelque chose d'autre. C'est un signe que HandleItem n'est pas vraiment quelque chose que vous pouvez "tester à l'unité" puisqu'il semble simplement déléguer à d'autres méthodes. Lorsque vous validez simplement que HandleItem appelle correctement les autres méthodes, cela devient davantage un test d’intégration (dans ce cas, vous auriez toujours 3 assertions).

Vous voudrez peut-être envisager de rendre publiques les 3 autres méthodes et de les tester indépendamment. Ou même les extraire à une autre classe.

2
ajouté

Je ne pense pas qu'il y ait de solides arguments en faveur de la rédaction de méthodes de test distinctes pour votre cas d'utilisation. Si vous souhaitez obtenir les résultats des trois conditions de variable, vous pouvez les tester et imprimer leurs échecs en les concaténant dans une chaîne et en affirmant si la chaîne est toujours vide une fois le test terminé. . En les conservant tous dans la même méthode, vos conditions et vos messages d'échec documentent la post-condition attendue de la méthode au même endroit, plutôt que de la scinder en trois méthodes qui pourraient être séparées ultérieurement.

Cela signifie que votre seul test aura plus de code, mais si vous avez tellement de post-conditions que c'est un problème, vous voudrez probablement refactoriser vos méthodes de test pour tester des méthodes individuelles dans HandleItem .

2
ajouté

IMHO, vous devriez tester les trois parties de cette méthode séparément afin de savoir plus précisément où tout va mal, tout en évitant de passer deux fois sur la même partie de votre code.

1
ajouté

Utilisez la 2ème approche. Avec votre 1ère approche, si le test échoue, vous ne saurez pas pourquoi tout de suite, car cela pourrait être l’une des 3 fonctions qui ont échoué. Avec la seconde approche, vous saurez tout de suite où le problème est survenu. Vous pouvez insérer du code en double dans votre fonction de configuration de test.

0
ajouté