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