web-dev-qa-db-fra.com

Test de l'unité Une classe qui appelle séquentiellement d'autres classes

Salut j'ai une classe comme ça

class MyClass {

  private ExternalClass1 ex1;
  private ExternalClass2 ex2;
  private ExternalClass3 ex3

public String doSomething(String arg1){ 
 val1=ex1.invoke(arg1); 
 val2=ex2.call(val1); 
 result=ex3.doit(val2);
 return result;
}    

}

test d'unité de cette méthode

@Test
void doSometing(){
   ExternalClass1 ex1=mock(ExternalClass1);
   ExternalClass2 ex2=mock(ExternalClass2);
   ExternalClass3 ex3=mock(ExternalClass2);

when(ex1.invoke(arg1)).thenReturn(val1);
when(ex2.call(val1)).thenReturn(val2);
when(ex3.doit(val2)).thenReturn(result);

Myclass myClass=new MyClass(ex1,ex2,ex3);

assertEquals(result,myClass.doSomething(arg1))
}

Le code de test de cette méthode semble être une simple répétition du code lui-même, mais plus complexe. Le test de ce type de classe dont le rôle est de contrôler d'autres classes apporte une valeur?

Merci

5
Belin

Apporte-t-il une valeur?

Oui - c'est une interaction de test.

Si vous avez déjà testé l'unité, les fonctionnalités des trois méthodes sont appelées, elles sont garanties correctement. Ce type de test est un test de l'état; Appelez la méthode et vérifiez que vous recevez le résultat correct.

Mais ici, vous voulez effectuer un test d'interaction; Celui qui teste que certaines méthodes ont été appelées correctement (vous ne savez peut-être pas ce que ces méthodes font pour les données, vous ne pouvez donc pas tester la valeur du résultat de manière cohérente).

Pour effectuer un test d'interaction, modifiez vos simulacres pour ajouter un incrémentant à chaque méthode, puis affirmez que les variables incrémentées sont ce que vous attendez:

La façon dont vous implémentez l'incrément dépendra de la manière dont vous avez créé vos cours et comment vous vous moquez.

Comme un bref exemple de Seudo:

@Test
void doSomething()
{
    ExternalClass1 ex1=mock(ExternalClass1);
    ExternalClass2 ex2=mock(ExternalClass2);
    ExternalClass3 ex3=mock(ExternalClass2);

    int ex1Count = 0;
    int ex2Count = 0;
    int ex3Count = 0;

    when(ex1.invoke(arg1)).do(ex1Count++).thenReturn(val1);
    when(ex2.call(val1)).do(ex2Count++).thenReturn(val2);
    when(ex3.doit(val2)).do(ex3Count++).thenReturn(result);

    Myclass myClass = new MyClass(ex1,ex2,ex3);

    myClass.doSomething(arg1);

    assertEquals(1, ex1Count);
    assertEquals(1, ex2Count);
    assertEquals(1, ex3Count);

    // assertEquals(2, ex1Count); // If for example ex3 utilises ex1 once (and you know that it should)...
}
6
Steve Padmore

La seule valeur qu'il apporte est la couverture de code, ce qui signifie que vous avez une couverture sur la méthode de dosagetage (). Est-ce que cela précieux? Peut-être sur un rapport de gestion, mais en ce qui concerne le test/la validation du comportement, il ne vaut rien.

La méthode de dosagetation () est une enveloppe ou une façade. Il ne contient aucune logique autre que les appels d'appels vers d'autres méthodes, il n'a donc pas besoin d'un test. D'autres tests sur ExternalClass1, ExternalClass2 et ExternalClass3 couvriront la fonctionnalité réelle.

2
Jon Raynor

Votre test fonctionnel peut avoir besoin d'appeler doSomething et de tester son être dans un ordre raisonnable. Vos tests de l'unité vérifient déjà toutes les parties qu'elle invoque.

Une chose que votre nité test peut tester pour doSomething est sa gestion des erreurs. Si l'un des trois appels peut échouer pour certaines entrées et que vous avez certaines attentes concernant des exceptions (ou non) soulevées pour de telles entrées non valides, cela peut valoir la peine d'être testée. Probablement les clients de doSomething dépendent de cela et ne veulent pas dépendre de ses détails de mise en œuvre.

1
9000

Nope! Non aussi.

Vous avez besoin de beaucoup plus Profondeur, ou beaucoup moins - En fonction de l'unité particulière en question. Comme il se trouve, votre test valide seulement que doSomething() renvoie la valeur de retour de ex3.doit().

Donc ...

Si vous en garde CommentdoSomething() fait son travail ... Ce n'est pas nécessairement Atypique Pour tester A n adaptateur , façade , ou tout type d'emballage de cette manière. Mais vous devez verrouiller cette implémentation et votre test tombe malveillante. Cela ne garantit pas en fait que L'emballage gère l'ensemble du processus correctement.

Pour atteindre ce niveau d'assurance, vous devez également valider que chaque méthode interne est invoquée lorsqu'il est attendu, comme prévu.

Cependant, il s'agit d'un besoin rare, dans mon expérience. J'ai seulement vraiment Dû valider les détails de mise en œuvre interne une fois ... ... Construire un enregistreur de demande d'API: J'avais besoin de vous assurer que l'enregistreur gère une connexion HTTP sous-jacente de manière très particulière. Et, je n'étais pas en liberté d'effectuer un test d'intégration complet ... Parce que je claquerais une API de production (en dehors de mon contrôle) avec des données de test.

Dans ce cas, j'ai effectivement construit des façons complètes des classes HTTP que j'utiliserais dans la production. J'ai enregistré chaque petite interaction entre mon enregistreur et la ressource qu'il gère. Et j'ai eu un tas de tests qui ont affirmé sur les détails de ces interactions.

Mais dans la plupart des cas, vos tests ne devraient pas veiller à ce que doSomething() fonctionne. Ils devraient juste se soucier que les effets et le résultats sont corrects.

Donc, si vous ne vous souciez pas de savoir comment doSomething() fait son travail ... Écrivez simplement un test d'intégration.

Un "test d'intégration" est toujours, à mon avis, un "test unitaire". L'unité se trouve être "grande".

1
svidgen