2010-07-19 5 views
25

J'essaie de se moquer d'une classe de Microsoft Sync Framework. Il a seulement un constructeur interne. Quand je les opérations suivantes:Mocking un type avec un constructeur interne en utilisant Moq

var fullEnumerationContextMock = new Mock<FullEnumerationContext>(); 

Je reçois cette erreur:

System.NotSupportedException: Parent does not have a default constructor. The default constructor must be explicitly defined.

Ceci est la trace de la pile:

System.Reflection.Emit.TypeBuilder.DefineDefaultConstructorNoLock(MethodAttributes attributes) System.Reflection.Emit.TypeBuilder.DefineDefaultConstructor(MethodAttributes attributes) System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() System.Reflection.Emit.TypeBuilder.CreateType() Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType() Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] interfaces, ProxyGenerationOptions options) Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, IInterceptor[] interceptors) Moq.Mock 1.<InitializeInstance>b__0() Moq.PexProtector.Invoke(Action action) Moq.Mock 1.InitializeInstance()

Comment puis-je travailler autour de cela?

+3

Merci pour cette question! C'est la simple existence m'a aidé. J'étais, sur mon propre code, capable de rendre le constructeur public. Malheureusement, cela ne peut pas vous aider, mais vous m'avez quand même aidé. +1 – Marcel

Répondre

17

Vous ne pouvez pas simuler un type qui n'a pas de constructeur public car Moq ne pourra pas instancier un objet de ce type. Selon ce que vous essayez de tester, vous avez quelques options:

  1. S'il y a un objet usine ou d'une autre façon d'obtenir des instances de FullEnumerationContext peut-être vous pouvez l'utiliser (désolé, je ne suis pas au courant du framework de synchronisation)
  2. Vous pouvez utiliser la réflexion privée pour instancier un FullEnumerationContext, mais vous ne pourrez pas vous moquer des méthodes.
  3. Vous pourriez introduire une interface et/ou un objet encapsuleur dont le code pourrait être invoqué. L'implémentation à l'exécution déléguerait au réel FullEnumerationContext, tandis que votre implémentation au moment du test effectuerait l'action dont vous avez besoin.
+3

En fait, vous pouvez instancier le faux pour une classe avec un constructeur interne. Vous avez juste besoin de l'attribut InternalsVisibleTo approprié appliqué à l'assemblage cible, comme expliqué dans "Fonctions avancées" dans https://code.google.com/p/moq/wiki/QuickStart – kzu

+11

@kzu Cela ne fonctionnera pas sur une bibliothèque tierce puisque vous ne construisez pas la bibliothèque vous-même. – DBueno

3

Je ne suis pas vraiment un expert sur Moq, mais je pense que vous devez spécifier les arguments pour le constructeur. Dans Rhino Mocks, vous devez les spécifier comme ceci:

var fullEnumerationContextMock = new Mock<FullEnumerationContext>(arg1, arg2); 

Il est probablement similaire dans Moq.

+1

Quelqu'un peut-il confirmer si c'est possible avec Moq? J'utilise Moq 3.1 – tjrobinson

+0

Non. Castle utilise le constructeur par défaut pour la création de classe par type. Au moins c'est ce que je lis ici [à GitHub] (https://github.com/castleproject/Core/blob/c06adf27bf7a0dfe94529a2563aca94bdedd1cb0/src/Castle.Core/DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs#L323) – durilka

+3

Ceci est seulement true si vous utilisez Moqing avec un constructeur public non défini par défaut. Pour un constructeur interne (par défaut ou autre), vous n'avez pas de chance. – RJFalconer

1

Sur la base des réponses de marcind que j'ai créé une interface (IFullEnumerationContext) que je Mock et j'ai deux surcharges de la méthode que je suis en train de tester, qui prend la FullEnumerationContext et un autre qui prend IFullEnumerationContext. Ce n'est pas génial, mais ça marche. Toutes les meilleures suggestions ou améliorations seraient les bienvenues.

public override void EnumerateItems(FullEnumerationContext context) 
{ 
    List<ItemFieldDictionary> listItemFieldDictionary = EnumerateItemsCommon(); 
    context.ReportItems(listItemFieldDictionary); 
} 

public void EnumerateItems(IFullEnumerationContext context) 
{ 
    List<ItemFieldDictionary> listItemFieldDictionary = EnumerateItemsCommon(); 
    context.ReportItems(listItemFieldDictionary); 
} 
+0

Je pense que ce serait mieux si la surcharge qui prend 'FullEnumerationContext' enveloppait l'instance de contexte dans un' FullEnumerationContextWrapper', puis passait cela dans la surcharge qui accepte 'IFullEnumerationContext'. De cette façon, une seule de ces méthodes contiendrait tout le code important. L'autre serait une déclaration d'une ligne qui n'a pas besoin d'être associée à un test unitaire. – marcind

0

En fait vous pouvez. Ouvrez votre fichier AssemblyInfo.cs et ajoutez la ligne suivante à la fin,

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

Questions connexes