2010-07-19 20 views
25

Estoy tratando de burlarme de una clase del Microsoft Sync Framework. Solo tiene un constructor interno. Cuando intento el siguiente:Burlando de un tipo con un constructor interno usando Moq

var fullEnumerationContextMock = new Mock<FullEnumerationContext>(); 

me sale este error:

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

Este es el seguimiento de la pila:

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

¿Cómo puedo trabajar alrededor de esto?

+3

Gracias por esta pregunta! Es mera existencia lo que me ayudó. Estaba, en mi propio código, capaz de hacer que el constructor sea público. Lamentablemente, esto no puede ayudarme en su caso, pero aún así me ayudó +1 – Marcel

Respuesta

17

No se puede simular un tipo que no tiene un constructor público porque Moq no podrá instanciar un objeto de ese tipo. Dependiendo de lo que está tratando de probar, usted tiene algunas opciones:

  1. Si hay un objeto de fábrica o de alguna otra manera de obtener las instancias de FullEnumerationContext tal vez usted puede usar ese (lo siento, no estoy familiarizado con el marco de sincronización)
  2. Puede usar el reflejo privado para crear una instancia de FullEnumerationContext, pero no podrá simular métodos.
  3. Puede introducir una interfaz y/o un objeto envoltorio que sea simulable que el código bajo prueba podría invocar. La implementación del tiempo de ejecución se delegaría en el FullEnumerationContext real, mientras que la implementación del tiempo de prueba realizaría cualquier acción que necesita.
+3

En realidad, PUEDES instanciar el simulacro de una clase con un constructor interno. Solo necesita el atributo InternalsVisibleTo adecuado aplicado al ensamblaje de destino, como se explica en "Funciones avanzadas" en https://code.google.com/p/moq/wiki/QuickStart – kzu

+11

@kzu Eso no funcionará en una biblioteca de terceros ya que usted no está construyendo la biblioteca usted mismo. – DBueno

3

No soy realmente un experto en Moq, pero creo que debe especificar los argumentos para el constructor. En burla de Rhino especificaría como esto:

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

probablemente Es similar en Moq.

+1

¿Alguien puede confirmar si esto es posible con Moq? Estoy usando Moq 3.1 – tjrobinson

+0

No. Eso no es así. Castle usa el constructor predeterminado para la creación de clase por tipo. Al menos eso es lo que leo aquí [en GitHub] (https://github.com/castleproject/Core/blob/c06adf27bf7a0dfe94529a2563aca94bdedd1cb0/src/Castle.Core/DynamicProxy/Generators/Emitters/AbstractTypeEmitter.cs#L323) – durilka

+3

Esto es solo verdadero si está Moqing algo con un constructor público no predeterminado. Para un constructor interno (predeterminado u otro), no tiene suerte. – RJFalconer

1

Sobre la base de las respuestas de marcind que he creado una interfaz (IFullEnumerationContext), que me burlo y luego tengo dos sobrecargas del método que estoy tratando de probar, que lleva la FullEnumerationContext y otra que toma IFullEnumerationContext. No se siente genial, pero funciona. Cualquier mejor sugerencia o mejora sería bienvenida.

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

Creo que sería mejor si la sobrecarga que toma 'FullEnumerationContext' envuelve la instancia de contexto en' FullEnumerationContextWrapper' y luego pasa eso a la sobrecarga que acepta 'IFullEnumerationContext'. De esta forma, solo uno de estos métodos contendría todo el código importante. El otro sería una declaración de una línea que no necesita tener una prueba de unidad asociada. – marcind

0

En realidad se puede. Abra el archivo de AssemblyInfo.cs y añadir la siguiente línea al final,

[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

Cuestiones relacionadas