Supposons que j'ai un gestionnaire de classe dérivé d'un employé de classe de base et que l'employé ait une méthode getEmail () héritée par le gestionnaire . Dois-je tester que le comportement de la méthode getEmail () d'un manager est en fait le même que celui d'un employé?
Au moment où ces tests sont écrits, le comportement sera le même, mais bien sûr, à un moment donné, quelqu'un pourrait remplacer cette méthode, changer son comportement et donc casser mon application. Cependant, il semble un peu étrange de tester essentiellement l' absence de code d'ingérence.
(Notez que le test de la méthode Manager :: getEmail () n'améliore pas la couverture du code (ou en fait toute autre métrique de qualité du code (?)) Jusqu'à ce que Manager :: getEmail () soit créé / remplacé.)
(Si la réponse est "Oui", certaines informations sur la façon de gérer les tests partagés entre les classes de base et dérivées seraient utiles.)
Une formulation équivalente de la question:
Si une classe dérivée hérite d'une méthode d'une classe de base, comment exprimez-vous (testez) si vous vous attendez à ce que la méthode héritée:
- Se comportent exactement de la même manière que la base en ce moment (si le comportement de la base change, le comportement de la méthode dérivée ne change pas);
- Se comportent exactement de la même manière que la base pour tous les temps (si le comportement de la classe de base change, le comportement de la classe dérivée change également); ou
- Comportez-vous comme il veut (vous ne vous souciez pas du comportement de cette méthode car vous ne l'appelez jamais).
Manager
classe aEmployee
été la première erreur majeure.Réponses:
Je prendrais l'approche pragmatique ici: si quelqu'un, à l'avenir, remplace Manager :: getMail, alors c'est la responsabilité du développeur de fournir le code de test pour la nouvelle méthode.
Bien sûr, cela n'est valable que s'il a
Manager::getEmail
vraiment le même chemin de code queEmployee::getEmail
! Même si la méthode n'est pas remplacée, elle peut se comporter différemment:Employee::getEmail
pourrait appeler un virtuel protégégetInternalEmail
qui est remplacé dansManager
.Employee::getEmail
pourrait accéder à un état interne (par exemple, un champ_email
), qui peut différer dans les deux implémentations: Par exemple, l'implémentation par défaut deEmployee
pourrait garantir que_email
c'est toujours[email protected]
, maisManager
est plus flexible dans l'attribution des adresses de messagerie.Dans de tels cas, il est possible qu'un bogue ne se manifeste que dans
Manager::getEmail
, même si l'implémentation de la méthode elle - même est la même. Dans ce cas, testerManager::getEmail
séparément pourrait avoir un sens.la source
Je voudrais.
Si vous pensez "eh bien, c'est vraiment seulement appeler Employee :: getEmail () parce que je ne le remplace pas, donc je n'ai pas besoin de tester Manager :: getEmail ()" alors vous ne testez pas vraiment le comportement de Manager :: getEmail ().
Je penserais à ce que seul Manager :: getEmail () devrait faire, pas s'il est hérité ou non. Si le comportement de Manager :: getEmail () doit être de retourner tout ce que Employee :: getMail () retourne, alors c'est le test. Si le comportement est de retourner "[email protected]", alors c'est le test. Peu importe qu'il soit implémenté par héritage ou remplacé.
Ce qui importe, c'est que, s'il change à l'avenir, votre test le détecte et vous savez que quelque chose s'est cassé ou doit être reconsidéré.
Certaines personnes peuvent être en désaccord avec la redondance apparente dans le chèque, mais mon contre serait que vous testiez le comportement Employee :: getMail () et Manager :: getMail () en tant que méthodes distinctes, qu'elles soient héritées ou non remplacé. Si un futur développeur doit changer le comportement de Manager :: getMail (), il doit également mettre à jour les tests.
Les opinions peuvent cependant varier, je pense que Digger et Heinzi ont donné des justifications raisonnables pour le contraire.
la source
Ne le testez pas à l'unité. Faites un test fonctionnel / d'acceptation.
Les tests unitaires devraient tester chaque implémentation, si vous ne fournissez pas une nouvelle implémentation, respectez le principe DRY. Si vous souhaitez y consacrer quelques efforts, vous pouvez améliorer le test unitaire d'origine. Ce n'est que si vous remplacez la méthode que vous devez écrire un test unitaire.
Dans le même temps, les tests fonctionnels / d'acceptation devraient s'assurer qu'à la fin de la journée, tout votre code fait ce qu'il est censé faire et, espérons-le, attrapera toute bizarrerie de l'héritage.
la source
Les règles de Robert Martin pour le TDD sont les suivantes:
Si vous suivez ces règles, vous ne serez pas en mesure de poser cette question. Si la
getEmail
méthode devait être testée, elle l'aurait été.la source
Oui, vous devez tester les méthodes héritées, car à l'avenir, elles pourraient être remplacées. En outre, les méthodes héritées peuvent appeler des méthodes virtuelles qui sont remplacées , ce qui changerait le comportement de la méthode héritée non substituée.
La façon dont je teste cela est en créant une classe abstraite pour tester la classe de base (éventuellement abstraite) ou l'interface, comme ceci (en C # en utilisant NUnit):
Ensuite, j'ai une classe avec des tests spécifiques à la
Manager
, et j'intègre les tests de l'employé comme ceci:La raison pour laquelle je peux le faire est le principe de substitution de Liskov: les tests de
Employee
devraient toujours passer quand on passe unManager
objet, car il dérive deEmployee
. Je dois donc écrire mes tests une seule fois, et je peux vérifier qu'ils fonctionnent pour toutes les implémentations possibles de l'interface ou de la classe de base.la source
setUp()
méthode de xUnit elle-même! Et à peu près tous les frameworks Web MVC qui impliquent de remplacer une méthode "index" cassent également LSP, qui est fondamentalement tous.class ManagerTests : ExployeeTests
? (c.-à-d. refléter l'héritage des classes testées.) Est-ce principalement esthétique, pour aider à visualiser les résultats?Je dirais non car ce serait un test répété à mon avis, je testerais une fois dans les tests des employés et ce serait tout.
Si la méthode est remplacée, vous aurez besoin de nouveaux tests pour vérifier le comportement substitué. C'est le travail de la personne qui met en œuvre la
getEmail()
méthode prioritaire .la source
Non, vous n'avez pas besoin de tester les méthodes héritées. Les classes et leurs cas de test qui s'appuient sur cette méthode s'arrêteront de toute façon si le comportement change
Manager
.Imaginez le scénario suivant: l'adresse e-mail est assemblée en tant que pré[email protected]:
Vous avez testé l'unité et cela fonctionne bien pour votre
Employee
. Vous avez également créé une classeManager
:Vous avez maintenant une classe
ManagerSort
qui trie les gestionnaires dans une liste en fonction de leur adresse e-mail. De votre part, vous supposez que la génération d'e-mails est la même que dansEmployee
:Vous écrivez un test pour votre
ManagerSort
:Tout fonctionne bien. Maintenant, quelqu'un vient et remplace la
getEmail()
méthode:Maintenant, que se passe-t-il? Votre
testManagerSort()
échouera parce que legetEmail()
deManager
a été surchargée. Vous allez enquêter sur ce problème et en trouver la cause. Et tout cela sans écrire un testcase séparé pour la méthode héritée.Par conséquent, vous n'avez pas besoin de tester les méthodes héritées.
Sinon, par exemple en Java, vous devrez tester toutes les méthodes héritées de
Object
liketoString()
,equals()
etc. dans chaque classe.la source