Tests unitaires: «C'est une odeur de code si vous refactorez et qu'il n'y a pas de collaborateurs»?

9

Je lis The Art of Unit Testing par Roy Osherove. Je suis à la section 7.2 Écriture de tests maintenables où l'auteur a cette note sur l'odeur de code:

REMARQUE: lorsque vous refactorisez l'état interne pour qu'il soit visible par un test externe, cela peut-il être considéré comme une odeur de code (signe que quelque chose ne va pas dans la conception ou la logique du code)? Ce n'est pas une odeur de code lorsque vous refactorisez pour exposer des collaborateurs. C'est une odeur de code si vous refactorisez et qu'il n'y a pas de collaborateurs (vous n'avez donc pas besoin de stub ou de se moquer de quoi que ce soit).

EDIT : Ce que l'auteur entend par «collaborateurs», ce sont les dépendances. Certains de ses exemples de dépendances sont des classes qui accèdent à une base de données ou qui accèdent au système de fichiers du système d'exploitation. Voici où il définit le talon et commence à utiliser le mot collaborateur:

Un stub est un remplacement contrôlable d'une dépendance (ou d'un collaborateur ) existante dans le système.

L'auteur n'a pas d'exemple de cette odeur de code et j'ai du mal à comprendre / imaginer à quoi cela ressemblerait. Quelqu'un peut-il expliquer un peu plus cela et peut-être fournir un exemple concret?

programmeur
la source
Je pense que la confusion vient ici du mot «collaborateurs». Je dois admettre que je ne sais pas non plus ce qu'il veut dire dans ce contexte.
rossipedia
@Bryan Ross, j'ai mis à jour le message avec la façon dont l'auteur utilise le mot "collaborateur". Merci!
programmeur

Réponses:

3

Je pense que c'est ce que l'auteur veut dire.

Dans mon exemple de code, j'ai une fenêtre de synchronisation qui prend une quantité de sortie plus une heure de début et de fin. L'objectif est de dessiner une fenêtre de sortie sur une période de 24 heures. Une ride est ajoutée lorsque l'heure de début est supérieure à l'heure d'arrêt car il s'agit d'une fenêtre de synchronisation qui s'étend sur minuit.

Vous pouvez écrire des tests unitaires qui exercent pleinement l'objet sans exposer les variables privées. Ces bools et intervalles de temps privés sont les collaborateurs auxquels il fait référence lors de l'exposition des composants internes pour les tests unitaires. Selon le livre, exposer ces internes ne serait PAS une odeur de code car ils sont des collaborateurs.

Exposer le double outputserait une odeur de code car ce n'est pas un collaborateur - c'est un élément explicitement caché par la classe elle-même qui possède une logique conditionnelle GetOutputpour déterminer ce qui doit être retourné.

Creuser dans les bools / timepans rendrait les tests unitaires plus complets. Il dit que c'est bien.
Creuser dans le double outputnécessiterait une logique supplémentaire dans votre test unitaire qui refléterait ce qui GetOutputse faisait. Ce serait l'odeur de code à laquelle il fait référence.

public class TimeWindow
{
  bool privé isConst;
  privé bool spansMidnight;
  privé TimeSpan start1;
  arrêt TimeSpan privé1;
  privé TimeSpan start2;
  arrêt TimeSpan privé2;
  double sortie privée;

  TimeWindow public (double out, TimeSpan start, TimeSpan stop)
  {
    sortie = sortie;

    if (start == stop)
      isConst = true;
    sinon si (démarrer> arrêter)
    {
      spansMidnight = true;
      start1 = minuit;
      stop1 = stop;
      start2 = start;
      stop2 = minuit;
    }
    autre 
    {
      start1 = start;
      stop1 = stop;
    }
  }

  public double GetOutput (TimeSpan time)
  {
    // un peu de logique ici sur quoi / comment retourner
    ...
    retour de sortie;
  }

}

la source
0

Disons que nous avons une classe de domaine, et cette classe de domaine a une connaissance directe de la couche de persistance à l'aide d'un référentiel, qu'il utilise afin d'exposer une méthode "Save" au niveau de l'instance que les objets travaillant sur la classe de domaine peuvent appeler pour conserver les changements fait sans avoir besoin de connaître le mécanisme (que ce soit une "bonne" conception est une discussion pour un autre jour). Refactoriser la classe pour exposer ce référentiel en tant qu'argument de propriété et / ou de constructeur, permettant ainsi de passer un référentiel simulé qui peut garantir que l'appel approprié est effectué, est généralement une bonne chose non seulement pour les tests, mais pour la maintenabilité générale.

Maintenant, étant une classe de domaine, elle contient des données d'état. Supposons un instant que l'une des propriétés avec état possède un champ de sauvegarde et que les accesseurs de propriété testent qu'une nouvelle entrée est valide en fonction de celle en cours (peut-être que la nouvelle valeur ne peut jamais être inférieure à l'ancienne). Vous devez tester cette validation, mais vous constatez que cela nécessite l'accès au champ de sauvegarde pour définir une valeur initiale que vous essayerez ensuite d'écraser. Ce devrait être un drapeau rouge; si le test a besoin d'accéder au champ de support (qui est un détail d'implémentation; aucun consommateur ne devrait jamaisdevez savoir qu'il est là) afin de mettre l'objet dans un état cohérent pour un test, alors comment le code de production obtiendrait-il un objet cohérent? S'il existe une méthode pour que le code de production fasse la même chose que votre test, alors le test devrait probablement imiter de cette façon. S'il n'y a pas de moyen valide pour que le code de production place l'objet dans cet état, alors pourquoi testez-vous ce scénario?

KeithS
la source