2009-04-18 9 views
2

J'ai quelques classes de base avec des méthodes de clonage:Unité Test d'une méthode clone avec Moq en C#

public class SimpleClass 
{ 
    public int ValueA { get; set; } 

    public string ValueB { get; set; } 

    public ulong ValueC { get; set; } 

    public SimpleClass TypedClone() 
    { 
     var item = new SimpleClass 
     { 
      ValueA = this.ValueA, 
      ValueB = this.ValueB, 
      ValueC = this.ValueC 
     }; 

     return item; 
    } 
} 

Je veux un test unitaire qui me dira si j'ajouter Valued, mais oublier d'ajouter au clone méthode. Ma première tentative consistait à utiliser Moq et sa méthode VerifyGet pour m'assurer que chaque propriété était accessible.

public void GenericCloneTest() 
    { 
     var mock = new Mock<SimpleClass>(); 
     var c = mock.Object.GenericClone(); 
     var properties = typeof(SimpleClass).GetProperties(); 

     foreach (var property in properties) 
     { 
      var expression = Expression.Property(
       Expression.Parameter(typeof(SimpleClass), "c"), 
       property); 

      var type = Expression.GetFuncType(typeof (SimpleClass), 
       property.PropertyType); 

      var actionExpression = Expression.Lambda(type, expression, 
       Expression.Parameter(typeof(SimpleClass), "c")); 

      mock.VerifyGet<object> 
       ((Expression<Func<SimpleClass,object>>)actionExpression); 
     } 
    } 

Cela ne fonctionne pas parce que la méthode VerifyGet a besoin de connaître le type de retour de l'accesseur de la propriété, et je ne peux pas comprendre quelque façon que pour l'insérer à l'exécution (vous remarquerez ma tentative boiteux utilisez "objet" qui s'est écrasé et brûlé).

Je ne suis même pas sûr que l'utilisation de Moq soit une bonne idée, c'était juste mon premier. MISE À JOUR: Sans méthode générique rapide et facile pour tester une méthode de clonage, je me suis contenté d'écrire des tests spécifiques au type pour chaque classe. Cela me laisse toujours le problème de savoir quand les propriétés ont été ajoutées. Je me suis contenté d'ajouter ceci à mes tests unitaires de clones:

 var signature = typeof (Connection) 
      .GetProperties() 
      .Select(p => p.Name) 
      .Aggregate(
       new StringBuilder(), 
       (builder, name) => 
        builder.Append(name)).ToString(); 

     Assert.AreEqual(
      "DataSessionStateDataTechnologyBytesReceivedBytesSentDuration", 
      signature); 

Si j'ajoute une propriété, le test échouera. Cela dépend toujours que je sois assez responsable pour réparer le reste du test lorsque la correspondance de signature échoue.

Répondre

1

L'un des moyens les plus simples pour vous assurer d'obtenir tous vos champs consiste simplement à utiliser la méthode MemberwiseClone(). Cela copiera automatiquement tous les champs de vos classes sur la nouvelle instance.

+0

Merci Paul! J'ai toujours pensé qu'il devait y avoir quelque chose de intégré à .NET comme ça, mais je ne l'avais pas encore trouvé. Cela sera utile partout où je veux faire une copie superficielle rapide. – MichaC

2

Vous ne voulez pas vraiment utiliser Moq ici, il vous suffit d'utiliser une réflexion. Fondamentalement écrire un test qui crée une instance de votre objet, définit chaque propriété, clone, puis s'assure que chaque propriété est égale sur le clone. Comme Paul l'a dit, consultez MemberwiseClone() qui le fait pour vous, ou au moins implémentez ICloneable, ce qui est une pratique standard pour ce faire. (MemberwiseClone() ne fait qu'une copie superficielle, c'est pourquoi vous pourriez avoir besoin d'implémenter ICloneable). Ensuite, vous pouvez même écrire un test qui examine votre assembly pour tout ce qui implémente ICloneable et le teste.

+0

J'ai commencé à y penser en utilisant juste la réflexion, mais la partie la plus difficile est l'étape où vous "définissez chaque propriété". Je dois définir chaque propriété à autre chose que la valeur par défaut pour vérifier la méthode clone, mais j'ai une collection de propriétés avec n'importe quel nombre de types. Je dois écrire du code qui sait comment changer et comparer tout type qu'il pourrait rencontrer. La question est de savoir si c'est plus de travail que de simplement écrire un test de clonage unique pour chaque type. – MichaC

+0

Ouais, c'est un équilibre de complexité dans un test sur beaucoup de code supplémentaire dans plusieurs tests ... toujours un jugement difficile. Dans ce cas, j'opterais pour un test complexe, car il vous épargnera beaucoup de tests répétitifs. Vous pouvez créer un simple dictionnaire ou une instruction switch avec différentes valeurs de test pour les différents types de données. –

+0

Pour le cas générique, où votre objet cloné peut contenir des références d'objet, c'est une chose très gênante pour le code w/reflection. Il ne suffit pas de simplement "définir chaque propriété", vous devez vous assurer que vous définissez quelque chose d'autre que la valeur par défaut. Mon équipe est allée de cette façon une fois; nous avons sélectionné une valeur "aléatoire" pour chaque propriété, en répétant jusqu'à ce que nous atteignions une valeur non-par défaut - Qui nécessitait de coder une méthode pour donner une valeur "aléatoire" pour ... tous les types de propriétés possibles, y compris nos objets personnalisés! Il a fallu plusieurs heures et était un cauchemar à comprendre et à maintenir. –

Questions connexes