2009-08-22 6 views
2

Mon équipe conçoit une bibliothèque qui encapsule des appels dans Active Directory pour rechercher et renvoyer une liste de personnes.Code Design/Testabilité Comment?

Nous avons une classe de personne qui résume l'information pour une personne trouvée. Nous utilisons ensuite la liste pour les envelopper. Lorsque nous appelons la recherche, elle utilise la bibliothèque System.Directory interne et renvoie un objet SearchResultCollection. Nous itérons ensuite à travers pour construire la liste <> et le retourner.

Nous avons conçu la classe personne pour n'avoir que des propriétés de lecture seule (obtenir) puisque nous ne voulons pas que l'appelé change l'information de personne. Nous passons dans l'objet SearchResult de la bibliothèque System.Directory sur le constructeur de la personne.

Mon problème est que nous ne pouvons pas tester cela facilement.

Mes pensées ont été jusqu'ici:

  1. variables passe dans le constructeur de personne pour chaque propriété devant être fixé. Malheureusement, cela va faire une très longue liste de paramètres constructeur .... Ça me fait mal.

  2. Autorise la classe de personne à avoir des paramètres sur les propriétés.

    Encore une fois, ça sent mauvais pour moi puisque nous ne pouvons pas empêcher l'appelé de l'utiliser.

  3. Refactor:

    J'ai regardé l'extrait à l'interface et d'adapter les techniques de paramètres. Il semble que le paramètre d'adaptation est le plus prometteur? Le paramètre Adapt semble sympa car il aide à rompre la dépendance que j'ai avec l'objet SearchResult de la bibliothèque d'annuaires. Donc, si à l'avenir je voulais faire un autre type de recherche, nous sommes en bonne forme. Au moins, je pense que nous sommes?

  4. Sous classe de l'objet de personne et de créer une personne de test avec setters ....

    On dirait que ça marcherait, mais pas sûr que ce soit la bonne façon de faire?

  5. Mock il

    n'avez pas fait encore se moquant à nouveau pas sûr de celui-ci.

EDIT: Si moqueur est la meilleure idée s'il vous plaît laissez-moi savoir ... Cependant, je serais curieux de savoir comment cela se fait sans se moquaient aussi (ou peut-être il n'est pas vraiment faire sans pouvoir se moquant) ....

J'apprécierais des conseils sur celui-ci.

Voici un extrait du code:

public class PeopleSearcher 
{ 
    .... declarations left out.... 

    public List<Person> FindPerson(string FirstName, string LastName, string Login) 
    { 
     ...filter setup left out for brevity.... 

     _peopleFound = _directoryToSearch.FindAll(); 
     //Convert to list of persons.... 
      int cnt = 0; 
      _listOfPeople = new List<Person>(); 
      while (cnt < _peopleFound.Count) 
      { 
       Person p = new Person(_peopleFound[0]); 
       _listOfPeople.Add(p); 
       cnt++; 
      } 
      return _listOfPeople; 
     } 

    } 

    public class Person 
    { 
     private string sn; 
     ....further declarations left out for brevity.... 

     public Person(SearchResult PersonFound) 
     { 
      sn = PersonFound.Properties["sn"].Count == 0 ? string.Empty : PersonFound.Properties["sn"][0].ToString(); 
      givenName = PersonFound.Properties["givenName"].Count == 0 ? string.Empty : PersonFound.Properties["givenName"][0].ToString(); 
      sAMAccountName = PersonFound.Properties["sAMAccountName"].Count == 0 ? string.Empty : PersonFound.Properties["sAMAccountName"][0].ToString(); 
      adsPath = PersonFound.Path == null ? string.Empty : PersonFound.Path; 

     } 

     public string LastName 
     { 
      get 
      { 
       return sn; 
      } 
     } 

     .... more getters... 
    } 
} 

Répondre

1

Et la plupart du temps les gens ou pas "moqueurs", ils font semblant ou cogner. Quoi qu'il en soit, votre 4ème option (sous-classe et ajouter des setters) me semble être le moyen le plus facile à utiliser étant donné votre base de code en supposant que vous voulez que les objets Person passent à d'autres méthodes. Parce que je ne pense pas que vous parlez de tester que l'objet personne obtient les propriétés définies par le constructeur, non?

+0

Correct. Je teste la liste des personnes renvoyées à partir de la recherche. Je ne m'inquiète pas du fait que le constructeur de personne fonctionne correctement. – klabranche

+0

Qu'en est-il du paramètre adapter? Je suis vraiment curieux de savoir si c'est un bon endroit pour faire ce genre de technique de refactoring? – klabranche

+1

Oui, briser la dépendance sur l'objet de la bibliothèque de répertoires est définitivement une meilleure façon de procéder. Et dans ce cas, vous n'avez pas besoin de sous-classer la classe de personne. Donnez-lui simplement un faux objet "adapté". Je pense que c'est ce que vous auriez probablement fini avec si le code a été créé en utilisant TDD. – Cellfish

0

Mock il. C'est le genre de situation pour laquelle on a inventé la moquerie. Je n'ai fait que me moquer de Ruby, donc je ne suis pas sûr de l'état de l'art pour .net, mais ça devrait bien fonctionner. Lorsque vous vous moquez de vous, vous pourriez réaliser certaines zones qui devraient être refactorisées. C'est aussi un bon plan."Mocking" est un mot qui est généralement utilisé pour toutes sortes de test doubles

+0

Si je devais éviter de me moquer juste pour le plaisir. Quelle serait la prochaine meilleure façon? – klabranche

+0

comme je le vois, vous finirez par vous moquer d'une manière ou d'une autre (même si vous n'utilisez pas explicitement un cadre fictif). Une façon de procéder est de sous-classer SearchResult et de remplacer l'initialiseur pour définir les propriétés comme étant une structure de données de votre propre conception. C'est toujours moqueur, juste un peu plus d'un hack. –

+0

Merci Ben. Ce fut l'une de mes idées et je suis d'accord que si c'est tout ce que je ferais pour cela, alors c'est un hack qu'un cadre moqueur est bien adapté. – klabranche

0

Dans votre simulation (par framework ou autre), vous allez devoir créer des objets Person avec des valeurs, ce qui vous ramène directement à votre problème d'origine.

Heureusement, il existe deux excellentes solutions:

1) Allez-y et ajouter setters à la classe de personne, mais les rendent protégés. Cela signifie que votre code de test et de simulation devrait être dans le même paquet, mais empêcherait les autres utilisateurs de muter vos Personnes. (Et nous ne voulons pas que des mutants courent - il y en a assez dans les films ces derniers temps).

2) Utilisez une classe Builder (comme décrit par Joshua Bloch dans Effective Java). Vous souhaitez créer une classe PersonBuilder statique public à l'intérieur personne, qui exporter une méthode de construction et spécificateurs de paramètres chainables (comme setters, mais pas séparément appelable):

 
public class Person .... 
    public static class PersonBuilder { 
     public PersonBuilder (String firstName, String lastName) {...} // my sample has two required values 
     public Person build() { ... } 
     public PersonBuilder ssn (String value) { ... } 
     public PersonBuilder adsPath (String value) { ... } 
     ... 
    } 
    ... 
} 

spécificateurs valeur chainables ressembler à ceci:

 
     public PersonBuilder ssn (String value) { 
     this.sn = value; 
     return this; 
     } 

Puis un appel pour créer une personne se présente comme suit:

 
    Person thisPerson = new Person.PersonBuilder ("John", "Smith").ssn("123-45-6789").adsPath("whatever"); 

cette méthode se cache complètement les méthodes qui peuvent définir les valeurs (en effet , vous n'avez pas de "setters"), mais vous évite d'avoir à gérer de longues listes d'arguments constructeurs (ce qui facilite le traitement des valeurs optionnelles). Par ailleurs, vous souhaitez probablement rendre privé le constructeur de Person.