【问题标题】:TargetInvocationException on mocking ChannelFactory with moq使用 moq 模拟 ChannelFactory 时出现 TargetInvocationException
【发布时间】:2016-05-24 10:26:23
【问题描述】:

为什么在模拟ChannelFactory<IService> 时会抛出TargetInvocationException

public interface IService
{
    void Do();
}

public class Service : IService
{
    private ChannelFactory<IRemoteService> factory;

    public Service(ChannelFactory<IRemoteService> factory)
    {
        this.factory = factory;
    }

    public void Do()
    {
        using (var remote = factory.CreateChannel())
        {
            remote.DoRemote();
        }
    }
}

[TestFixture]
public class ChannelFactoryMoqTest
{
    private IService service;
    private Mock<ChannelFactory<IRemoteService>> factory;

    [Test]
    public void Test()
    {
        this.factory = new Mock<ChannelFactory<IRemoteService>>();

        this.service = new Service(this.factory.Object);
        // Calling Object on this.factory throws TargetInvocationException
    }
}

我想使用ChannelFactory 依赖而不是简单的IRemoteService,因为在我看来,每次调用创建Service 实例在并发方面更安全。

这是异常堆栈跟踪:

  System.Reflection.TargetInvocationException was unhandled by user code
  HResult=-2146232828
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
       at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
       at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)
       at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)
       at System.Activator.CreateInstance(Type type, Object[] args)
       at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyInstance(Type proxyType, List`1 proxyArguments, Type classToProxy, Object[] constructorArguments)
       at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
       at Moq.Proxy.CastleProxyFactory.CreateProxy(Type mockType, ICallInterceptor interceptor, Type[] interfaces, Object[] arguments)
       at Moq.Mock`1.<InitializeInstance>b__2()
       at Moq.PexProtector.Invoke(Action action)
       at Moq.Mock`1.InitializeInstance()
       at Moq.Mock`1.OnGetObject()
       at Moq.Mock.GetObject()
       at Moq.Mock.get_Object()
       at Moq.Mock`1.get_Object()
       at TR.Eikon.Services.AppVersion.Tests.Workflows.ChannelFactoryMoqTest.Test() in d:\Temp.cs:line 61
  InnerException: System.NullReferenceException
       HResult=-2147467261
       Message=Object reference not set to an instance of an object.
       Source=System.ServiceModel
       StackTrace:
            at System.ServiceModel.ChannelFactory.EnsureSecurityCredentialsManager(ServiceEndpoint endpoint)
            at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
            at System.ServiceModel.ChannelFactory`1..ctor()
            at Castle.Proxies.ChannelFactory`1Proxy..ctor(IInterceptor[] )
       InnerException: 

【问题讨论】:

  • 显示异常的内容。 Moq 很可能无法创建可行的模拟,因为该类不是抽象的。您还可以考虑使用更 mockableIChannelFactory&lt;T&gt;
  • @Nkosi 我最终使用了 IChannelFactory,尽管它没有与 CreateChannel 相同的签名查看异常,它看起来与非抽象类不同。
  • 如果您查看内部异常之前的堆栈跟踪,您会看到 Moq 尝试使用默认构造函数创建一个实例,该构造函数在尝试使用预期从配置文件。这是预期的,因为当您使用默认构造函数时,它会在配置文件中查找设置。

标签: .net wcf mocking moq channelfactory


【解决方案1】:

您收到该错误是因为 ChannelFactory 正在尝试为与其关联的非现有端点创建实际代理。

我的建议是将ChannelFactory&lt;IRemoteService&gt; 与您控制的接口后面的客户端隐藏/隔离,以便您可以更好地管理所需的API。通过这种方式,您可以替换工厂,为客户端提供创建模拟而不是真实代理的工厂。

public interface IMyChannelFactory<TChannel> {
    TChannel CreateChannel();
}

...以后可以包装ChannelFactory&lt;IRemoteService&gt;...的实际实例...

public class ChannelFactoryWrapper<TChannel> : IMyChannelFactory<TChannel> {
    private ChannelFactory<TChannel> factory;
    public ChannelFactoryWrapper(ChannelFactory<TChannel> factory) {
        this.factory = factory;
    }

    public TChannel CreateChannel() {
        return factory.CreateChannel();
    }
}

您重构的服务类将引用您的新 mockable 接口...

public class Service : IService {
    private IMyChannelFactory<IRemoteService> factory;

    public Service(IMyChannelFactory<IRemoteService> factory) {
        this.factory = factory;
    }

    public void Do() {
        using (var remote = factory.CreateChannel()) {
            remote.DoRemote();
        }
    }
}

...然后您的测试可以被重构并相应地测试...

[Test]
public void Should_Mock_ChannelFactory() {
    //Arrange
    var remoteService = new Mock<IRemoteService>();
    remoteService.Setup(m => m.DoRemote()).Verifiable();
    var factory = new Mock<IMyChannelFactory<IRemoteService>>();
    factory.Setup(f => f.CreateChannel()).Returns(remoteService.Object).Verifiable();
    var service = new Service(factory.Object);

    //Act
    service.Do();

    //Assert
    remoteService.Verify();
    factory.Verify();
}

【讨论】:

  • 非常有用的帖子,谢谢!我认为您必须(在某处)添加ChannelFactory&lt;IRemoteService&gt; 的具体实现以完成。
  • @Max 那个实现来自微软docs.microsoft.com/en-us/dotnet/api/…
猜你喜欢
  • 2011-06-27
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
  • 2011-10-26
  • 1970-01-01
  • 2011-06-13
  • 2019-09-11
  • 2010-11-16
相关资源
最近更新 更多