Search code examples
.netwcfmockingmoqchannelfactory

TargetInvocationException on mocking ChannelFactory with moq


Why TargetInvocationException is thrown when mocking ChannelFactory<IService>?

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

I want to use ChannelFactory dependency instead of simple IRemoteService cause it seems to me it's safer in terms of concurrency to create Service instance per call.

Here is the exception stack trace:

  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: 

Solution

  • You are receiving that error because ChannelFactory is trying to create actual proxies to non existing endpoints associated with it.

    My suggestion would be hide/isolate the ChannelFactory<IRemoteService> from the client behind interfaces you control so you can better manage the API you want. This way you can replace the factory, providing the client one which creates mocks instead of real proxies.

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

    ...that can later wrap actual instances of ChannelFactory<IRemoteService>...

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

    Your refactored service class would reference your new mockable interface...

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

    ...and your test could then be refactored and tested accordingly...

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