2010-07-19 13 views
25

Sto provando a prendere in giro una classe da Microsoft Sync Framework. Ha solo un costruttore interno. Quando provo il seguente:Mocking di un tipo con un costruttore interno utilizzando Moq

var fullEnumerationContextMock = new Mock<FullEnumerationContext>(); 

ottengo questo errore:

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

Questa è l'analisi dello stack:

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()

Come posso lavorare intorno a questo?

+3

Grazie per questa domanda! È la semplice esistenza che mi ha aiutato. Ero, sul mio codice, in grado di rendere pubblico il costruttore. Purtroppo questo non può aiutarti nel tuo caso, ma mi hai ancora aiutato +1 – Marcel

risposta

17

Non è possibile prendere in giro un tipo che non ha un costruttore pubblico perché Moq non sarà in grado di creare un'istanza di un oggetto di quel tipo. A seconda di cosa si sta cercando di testare, avete alcune opzioni:

  1. Se c'è un oggetto in fabbrica o in qualche altro modo per ottenere istanze di FullEnumerationContext forse si può usare quella (mi dispiace, io non sono a conoscenza del sync framework)
  2. È possibile utilizzare la riflessione privata per creare un'istanza di FullEnumerationContext, ma in questo caso non si sarà in grado di simulare i metodi su di esso.
  3. È possibile introdurre un oggetto di interfaccia e/o wrapper che è discutibile che il codice in prova possa richiamare. L'implementazione runtime delegherebbe al real FullEnumerationContext, mentre l'implementazione in fase di test eseguirà qualsiasi azione tu abbia bisogno.
+3

In realtà, è possibile creare un'istanza di simulazione per una classe con un costruttore interno. Hai solo bisogno dell'attributo InternalsVisibleTo corretto applicato all'assieme di destinazione, come spiegato in "Funzioni avanzate" in https://code.google.com/p/moq/wiki/QuickStart – kzu

+11

@kzu Che non funzionerà su una libreria di terze parti poiché non stai costruendo la libreria da solo. – DBueno

3

Non sono un vero esperto di Moq, ma penso che sia necessario specificare gli argomenti per il costruttore. In Rhino Mocks dovresti specificarli in questo modo:

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

Probabilmente è simile in Moq.

+1

Qualcuno può confermare se ciò è possibile con Moq? Sto usando Moq 3.1 – tjrobinson

+0

No. Questo no. Castle utilizza il costruttore predefinito per la creazione classe per tipo. Almeno questo è quello che ho letto qui [su GitHub] (https://github.com/castleproject/Core/blob/c06adf27bf7a0dfe94529a2563aca94bdedd1cb0/src/Castle.Core/DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs#L323) – durilka

+3

Questo è solo vero se sei Moqing qualcosa con un costruttore pubblico non predefinito. Per un costruttore interno (predefinito o meno) sei sfortunato. – RJFalconer

1

Sulla base delle risposte da marcind ho creato un'interfaccia (IFullEnumerationContext) che ho finto e poi ho due overload del metodo che sto cercando di testare, uno che prende il FullEnumerationContext e un altro che prende IFullEnumerationContext. Non sembra fantastico, ma funziona. Eventuali suggerimenti o miglioramenti migliori sarebbero i benvenuti.

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

Penso che sarebbe meglio se il sovraccarico che richiede 'FullEnumerationContext' ha avvolto l'istanza di contesto in un' FullEnumerationContextWrapper' e poi lo ha passato nel sovraccarico che accetta 'IFullEnumerationContext'. In questo modo solo uno di questi metodi conterrebbe tutto il codice importante. L'altra sarebbe una dichiarazione di una riga che non ha bisogno di avere un test unitario ad esso associato. – marcind

0

In realtà è possibile. Aprire il vostro file AssemblyInfo.cs e aggiungere la seguente riga alla fine,

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]