【问题标题】:Mocking a type with an internal constructor using Moq使用 Moq 使用内部构造函数模拟类型
【发布时间】:2011-03-17 19:16:49
【问题描述】:

我正在尝试从 Microsoft Sync Framework 模拟一个类。它只有一个内部构造函数。当我尝试以下操作时:

var fullEnumerationContextMock = new Mock<FullEnumerationContext>();

我收到此错误:

System.NotSupportedException:父级 没有默认构造函数。 默认构造函数必须是 明确定义。

这是堆栈跟踪:

System.Reflection.Emit.TypeBuilder.DefineDefaultConstructorNoLock(MethodAttributes 属性) System.Reflection.Emit.TypeBuilder.DefineDefaultConstructor(MethodAttributes 属性) System.Reflection.Emit.TypeBuilder.CreateTypeNoLock() System.Reflection.Emit.TypeBuilder.CreateType() Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType() Castle.DynamicProxy.Generators.ClassProxyGenerator.GenerateCode(Type[] 接口,ProxyGenerationOptions 选项) Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxy(类型 classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions 选项) Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions 选项) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(类型 classToProxy,Type[] additionalInterfacesToProxy, ProxyGenerationOptions 选项,Object[] constructorArguments, IInterceptor[] 拦截器) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(类型 classToProxy,Type[] additionalInterfacesToProxy, ProxyGenerationOptions 选项,IInterceptor[] 拦截器) Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(类型 classToProxy,Type[] additionalInterfacesToProxy,IInterceptor[] 拦截器) Moq.Mock1.<InitializeInstance>b__0() Moq.PexProtector.Invoke(Action action) Moq.Mock1.InitializeInstance()

我该如何解决这个问题?

【问题讨论】:

  • 感谢您提出这个问题!仅仅是存在帮助了我。我可以在我自己的代码中公开构造函数。不幸的是,这对你的情况没有帮助,但你仍然帮助了我 +1

标签: c# unit-testing moq microsoft-sync-framework


【解决方案1】:

在您的 System Under Test 项目中,您需要:

  • protected internal SUT 类中的构造函数可见性(例如 FullEnumerationContext
  • AssemblyInfo.cs 应该将其内部结构暴露给测试项目:

    [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
    

【讨论】:

    【解决方案2】:

    其实你可以。打开您的 AssemblyInfo.cs 文件并在末尾添加以下行,

    [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

    【讨论】:

      【解决方案3】:

      根据marcind 的答案,我创建了一个接口 (IFullEnumerationContext),我对其进行了模拟,然后我尝试测试的方法有两个重载,一个采用 FullEnumerationContext 和另一个需要IFullEnumerationContext。感觉不是很好,但确实有效。欢迎任何更好的建议或改进。

      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);
      }
      

      【讨论】:

      • 我认为如果采用FullEnumerationContext 的重载将上下文实例包装在FullEnumerationContextWrapper 中,然后将其传递到接受IFullEnumerationContext 的重载中会更好。这样一来,这些方法中只有一个会包含所有重要的代码。另一种是单行语句,不需要关联单元测试。
      【解决方案4】:

      您不能模拟没有公共构造函数的类型,因为 Moq 将无法实例化该类型的对象。根据您要测试的内容,您有几种选择:

      1. 如果有工厂对象或其他获取 FullEnumerationContext 实例的方法,也许您可​​以使用它(抱歉,我不熟悉同步框架)
      2. 您可以使用私有反射来实例化 FullEnumerationContext,但是您将无法在其上模拟方法。
      3. 您可以引入一个可模拟的接口和/或包装对象,供被测代码调用。运行时实现将委托给真正的 FullEnumerationContext,而您的测试时实现将执行您需要的任何操作。

      【讨论】:

      • 实际上,您可以使用内部构造函数为类实例化模拟。您只需要将正确的 InternalsVisibleTo 属性应用于目标程序集,如code.google.com/p/moq/wiki/QuickStart中的“高级功能”中所述
      • @kzu 这不适用于第三方库,因为您不是自己构建库。
      【解决方案5】:

      我并不是真正的起订量专家,但我认为您需要为构造函数指定参数。在 Rhino Mocks 中,您可以这样指定它们:

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

      起订量可能类似。

      【讨论】:

      • 谁能确认起订量是否可行?我正在使用起订量 3.1
      • 没有。那不是。 Castle 使用默认构造函数按类型创建。至少这是我在这里读到的at GitHub
      • 只有当你使用公共的非默认构造函数来 Moqing 时,这才是正确的。对于内部构造函数(默认或其他),你不走运。
      猜你喜欢
      • 2011-02-27
      • 1970-01-01
      • 2011-11-16
      • 2012-06-16
      • 2014-06-03
      • 2014-05-21
      • 1970-01-01
      • 1970-01-01
      • 2019-03-28
      相关资源
      最近更新 更多