Sonic Soul
Sonic Soul

Reputation: 24949

partial mocking with Moq and Castle Windsor

versions    Castle Windsor 3.3.0.0    Moq 4.2.1507.118

it appears that Moq comes with CallBase setting that, when enabled, delegates calls to any non-setup methods down to base object.

var mock = new Mock<MyService> {CallBase = true};

unfortunately as soon as I register it with Castle Windsor, all non mocked methods are returning a null immediately, underlying logic is no longer called.

         Container.Register(Component
            .For<MyService>()
            .Instance(mock.Object)
            .LifeStyle.Singleton
            );

I tried a few different lifestyles as well.

so when I do this:

mock.setup(x => x.SomeMethod).Returns(myMockedData);

this works as expected:

Container.Resolve<MyService>().SomeMethod();

but now this returns a null:

Container.Resolve<MyService>().AnotherMethod(); 

as soon as I remove the mock registration, AnotherMethod starts working as before.

more specific code example

public class Auth : IAuth
{
   // mocked succesfully
   public IEnumerable<T> AuthorizeData<T>(IEnumerable<T> data)  { ... }

    // never gets called
    public virtual List<User> GetAllUsers() {
        return new List<User>{new User{FIRST_NAME = "test"}};
    }
}

mock method

    protected static Mock<T> GetMock<T>() where T : class {

        var mock = new Mock<T> {CallBase = true, DefaultValue = DefaultValue.Mock};
        IoC.Container.Register(Component
            .For<T>()
            .Instance(mock.Object)
            .LifeStyle.Transient
            );
        return mock;
    }

mock setup

GetMock<IAuth>().Setup(x => x.AuthorizeData(It.IsAny<IEnumerable<DO>>()))
                .Returns((IEnumerable<DO> data) => data); // pass through input data (do not authorize

to sum up again, AuthorizeData is mocked successfully, GetAllUsers() returns an empty list. and original class never gets called

Upvotes: 5

Views: 1938

Answers (1)

Mark Seemann
Mark Seemann

Reputation: 233347

CallBase doesn't control how return values are generated. For that, you'll need to set the DefaultValue property:

var mock = new Mock<MyService> { CallBase = true, DefaultValue = DefaultValue.Mock };

The default value for DefaultValue (sic.) is DefaultValue.Empty, so that's the reason you'll need to explicitly set it to DefaultValue.Mock.

When you do that, the mock will return other proxy values from methods without explicit setups. If the return value is a concrete type, Moq will attempt to create a value of that type, but it'll only do that if that type has a parameterless constructor. If no parameterless constructor exists, it'll throw an exception when you try to call the method.


You may want to look into turning Castle Windsor into an Auto-Mocking Container.


Based on the comments below, I can't reproduce the issue. Assuming that AuthorizeData is virtual (which it isn't above, but which the comments claim), this test passes:

[Fact]
public void Repro()
{
    var td = new Mock<Auth>
    {
        CallBase = true,
        DefaultValue = DefaultValue.Mock
    };
    var container = new WindsorContainer();
    container.Register(Component.For<Auth>().Instance(td.Object));

    td
        .Setup(a => a.AuthorizeData<string>(new string[0]))
        .Returns(new[] { "foo", "bar" });

    Assert.Equal(
        new[] { "foo", "bar" },
        container.Resolve<Auth>().AuthorizeData(new string[0]));
    Assert.NotEmpty(container.Resolve<Auth>().GetAllUsers());
}

Upvotes: 2

Related Questions