web-dev-qa-db-fra.com

Test unitaire d'une méthode qui appelle une autre méthode

Quelle est la meilleure façon de tester à l'unité une méthode qui fait appel à plusieurs méthodes, par exemple:

modify(string value)
{
    if(value.Length > 5)  replaceit(value);

    else changeit(value);
}

Ce pseudo-code a une méthode de modification qui appelle (actuellement) replaceit() ou changeit(). J'ai déjà écrit des tests pour replaceit et changeit, donc écrire un nouveau test pour modifier sera à 99% le même ensemble de code. J'ai besoin de le tester parce qu'il pourrait changer à l'avenir.

Alors est-ce que je copie-colle le code de test existant? Déplacer le code de test vers une fonction commune? D'autres idées? Je ne suis pas sûr de la meilleure pratique ici.

52
NotDan

Il s'agit d'un test classique basé sur l'état par rapport à un scénario de test basé sur le comportement.

Dans cet exemple ridiculement simple, le test de la sortie est correct. À un certain moment cependant, vous rencontrerez des tests où l'inspection de l'état après l'exécution est compliquée. Au lieu de cela, vous souhaitez vérifier le comportement (par exemple, vérifiez que changeit a été appelé avec une valeur spécifique).

À ce stade, vous devriez probablement examiner un cadre d'objet simulé comme Rhino.Mocks (.Net) ou Mockito (Java) et commencer à écrire plus de code basé sur l'interface.

36
Eric Nicholson

Vous disposez d'un certain nombre d'options. Laquelle est la meilleure dépend de détails qui ne sont pas clairs à partir de votre question.

  • test modify comme s'il s'agissait d'une méthode indépendante. Avantage: il pourrait à un moment donné en devenir un.
  • testez simplement que vous avez obtenu la bonne instruction if. Autrement dit, testez juste assez pour que les tests vous forcent à écrire l'implémentation dont vous avez besoin (où appeler replaceit et changeit n'est que le implémentation la plus simple qui pourrait fonctionner. Si vous pratiquez le TDD, cela devrait vous venir naturellement.Avantage: une couverture de test élevée sans trop d'efforts en double.
  • Subclass and Override Method (il s'agit d'une technique de rupture des dépendances du livre "Working Effectively With Legacy Code"): Testez la méthode sur une sous-classe que vous introduisez uniquement à des fins de test, ce qui remplace replaceit et changeit avec des réponses prédéfinies ou pour qu'elles définissent des variables de détection (variables qui indiquent si la méthode a été appelée avec la ou les bonnes valeurs). Avantage: pourrait éventuellement simplifier vos tests (ou pas), parfois même simplement rendre les tests possibles.
  • Extrayez une nouvelle classe pour les méthodes replaceit et changeit, y compris une interface pour cette classe. Stub ou Mock cette interface lors du test de modify. Avantage: pourrait à la fois rendre votre conception plus testable et mieux découplé/réutilisable en général (ou non).
15
Ilja Preuß

Si vous avez déjà testé replaceit() et changeit() indépendamment, la seule chose qu'il vous reste à tester est la condition if. Testez modify() avec quelques valeurs pour vous assurer qu'elle appelle la bonne fonction dans les bonnes conditions (ces conditions étant null et Strings de longueur 4, 5 et 6 pour l'exemple de code que vous avez donné).

15
Bill the Lizard

Testez simplement modify.

Modify est censé retourner certaines valeurs lorsque certaines valeurs sont données.

Ce n'est pas important comment modifier fait son travail - seulement qu'il fait son travail.

Et si, à l'avenir, vous modifiez modify pour utiliser différentes méthodes (ou aucune méthode), cela n'affecte pas et ne devrait pas affecter vos tests.

Cela dit, testez également replaceit' andchangeit`.

6
Ian Boyd

Par ordre de préférence

  1. modify (test) n'a que 2 scénarios (chaque bras du if stmt), donc j'écrirais 2 tests pour modifier le formulaire.
    Si le résultat attendu de replaceit (valeur) est facile à déterminer ..

.

public TestModifyIfValueLength..()
    {
      string expectedValue = .. ;// literal result of replaceit(value)
      Assert.Equals( expectedValue, modify("asd") );
    }
  1. Sinon, envisagez d'utiliser un stub (utilisez la sous-classe et remplacez changeit, replaceit) pour vérifier que la bonne méthode a été appelée.
  2. Si le talon est trop de travail, faites la chose Mock. Extraire une interface et configurer les attentes sur changeit, replaceit.

Hypothèses

  • Vous avez des tests pour replaceit (valeur) et changeit (valeur), qui testent (par exemple toutes les conditions aux limites pour) ces 2 méthodes de manière complète.
  • replaceit () et changeit () sont des méthodes publiques. Sinon, vous devriez envisager d'écrire des tests uniquement avec les méthodes publiques. Vous devriez être libre de Tweak/chuck out des méthodes privées sans que le code de test ne le sache.
5
Gishu

Quel est "le code de test" dans ce cas? Configuration et vérification des résultats? Si c'est le cas, je le refactoriserais dans une méthode différente et l'utiliserais à partir de chacun des tests. Je ne ferais cela que s'il y en a une quantité significative - il y a un avantage de lisibilité à pouvoir voir tout ce qu'un test fait, juste en lisant le code de cette méthode.

Pour être honnête, les méthodes de test compliquées me dérangent souvent au départ - souvent, elles ne peuvent pas être évitées de manière réaliste, mais si vous pouvez les simplifiez, cela vaut la peine.

4
Jon Skeet

Eh bien, non, votre code de test ne sera pas identique à 99%, car vous testez en fait quelque chose de différent ici, à moins que replaceit, changeit et modify renvoient tous les mêmes valeurs.

Je ne sais pas pourquoi la difficulté. Le test de la méthode de modification doit comporter environ quatre lignes. Puisque vous testez déjà la fonctionnalité sous-jacente et que tout ce que vous voulez faire est de vous assurer que cette méthode particulière ne casse pas, l'écriture d'un test qui teste les deux chemins de code possibles dans cette fonction renvoie les valeurs attendues devrait être suffisante.

2
TheSmurf

Si vous avez déjà écrit des tests pour replaceit () et changeit (), le test de modify vérifierait simplement que des résultats différents sont renvoyés en fonction de la valeur de 'value'. Cependant, vous réimplémenterez simplement la logique de la méthode dans le test, ce qui est un peu absurde.

Dans ce cas, je ne testerais pas la modification jusqu'à ce qu'elle ait une logique plus complexe, ou mieux - soit utilisée par une autre méthode plus importante à tester.

2
Eran Galperin

Vous avez essentiellement besoin de 2 tests.

1) Passez une chaîne comme "The Quick Brown Fox Jumps!" (longueur supérieure à cinq) s'assure que la valeur est affectée par replaceit(...)

2) Passez une chaîne comme "Foo" (la longueur est inférieure à cinq) et assurez-vous que la valeur est affectée par changeit(...)

Votre test (en pseudo code) pourrait ressembler à ceci:

testLongValue() {
    string testValue = "A value longer than 5 chars";
    string expected = "Replaced!";
    string actual = modify(testValue);
    assertEqual(expected, actual);
}

testShortValue() {
    string testValue = "len4";
    string expected = "Changed!";
    string actual = modify(testValue);
    assertEqual(expected, actual);
}

Évidemment, je pourrais vous donner un exemple plus réaliste si je savais ce que replacit () et changeit () étaient censés faire, mais cela devrait vous donner l'idée. S'il mute la référence de valeur d'origine au lieu de la renvoyer, vous pouvez simplement utiliser testValue comme valeur réelle après l'appel.

2
Justin Standard

Lorsque vous testez des conditions aux limites comme if (value.length > 5), vous devez vous assurer que vos données de test contiennent des valeurs de value qui ont une longueur 4, 5, ou 6.

2
Brian Matthews

Vous pouvez créer une fonction à partir des méthodes et vous moquer de ces fonctions. Ou, vous pouvez créer des méthodes virtuelles et en utilisant des simulations de Rhino - simulation partielle, vous pouvez simuler ces méthodes virtuelles.

2
Saravanan

Identique à Justin Standard, plus en passant null comme valeur (ce qui échouera évidemment pour l'extrait de code que vous nous fournissez;)) La règle de base pour les tests unitaires est "tester uniquement ce qui est spécifique à la méthode testée ". Et c'est assez ... rare d'avoir une méthode qui n'en appelle pas une autre.

0
Olivier