简体   繁体   中英

Using Moq in Castle.Windsor factory method throws

I have encountered a problem while using Moq (v4.8.1) in a factory method for Castle.Windsor (v4.1.0). I would like to know if anyone else has encountered this, and what they did to solve it.

I have a simple interface defined as:

interface IFoo
{
    Task DoFooAsync(Bar bar);
}

In my main project I register the following in my WindsorContainer:

container
    .Register(Component.For<IFoo, Foo>()
    .LifestyleTransient());

This resolves nicely at runtime.

In my test project I do the following:

container.Register(Component.For<IFoo>().UsingFactorymethod(() => {
    var mock = new Mock<IFoo>();
    mock
        .Setup(x => x.DoFooAsync(It.IsAny<Bar>()))
        .Returns(() => Task.FromResult(true));
    return mock.Object;
})
.IsDefault());

This fails during test with the message from Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.ApplyCommissionConcerns(Object instance):

" Can not apply commission concerns to component Late Bound MyAssembly.IFoo because it appears to be a target-less proxy. Currently those are not supported. "

Note: The 'IsDefault()' is only there to make Castle.Windsor override the registration from the main project, and it is not related to this problem (I tried running without it and the problem persists).

If I instead do the following:

// exact same code as above, just placed outside the factory method
var mock = new Mock<IFoo>();
mock
    .Setup(x => x.DoFooAsync(It.IsAny<Bar>()))
    .Returns(() => Task.FromResult(true));

// different registration
container
    .Register(Component.For<IFoo>()
    .Instance(mock.Object).IsDefault());

Then everything works. But now my IFoo instance is reused each time IFoo is resolved - which is a different behavior than in my main project, and therfore not desired.

I have looked into the Castle.Windsor code to understand what is going on, but without real success.

Moq internally uses Castle's dynamic proxy and mocked class is in fact proxy generated by Castle and that's what for some internal reasons Castle's developers don't want to support. You can read about it more here:

https://github.com/castleproject/Windsor/issues/224

https://github.com/castleproject/castle-youtrack-export/blob/master/IOC/IOC-332.xml

So as Nkosi already commented, you should rather think about what you really need to achieve and reconsider your design.

"my IFoo instance is reused each time IFoo is resolved - which is a different behavior than in my main project, and therfore not desired."

What about creating a property with a lambda? Something like:

private IFoo TheMock => CreateMock();
private IFoo CreateMock()
{
   var mock = new Mock<IFoo>();
   mock
    .Setup(x => x.DoFooAsync(It.IsAny<Bar>()))
    .Returns(() => Task.FromResult(true));
   return mock.Object;
}

And then register it

container
.Register(Component.For<IFoo>()
.Instance(TheMock).IsDefault());

Disclaimer: Haven't tested that, because I do not use Castle.Windsor.

This works for me. UsingFactoryMethod() was failing. It uses Nsubstitute for fake injection.

var blobservice = Substitute.For<IBlobFileService>();           
RegisterFakeServiceInstance(blobservice);
private void RegisterFakeServiceInstance<TService>(TService fakeService) where TService : class
{
    IocManager.IocContainer.Register(
    Component.For<TService>()
           .Instance(fakeService)
           .LifestyleSingleton()
    );
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM