AutoFixture AutoMoq not creating mocks for some properties
Asked Answered
F

1

5

I am using AutoFixture with the AutoMoqCustomization and attempting to create an instance of a class which contains a readonly property thus:

public override ILog Logger { get; } = LogManager.GetLogger(typeof(MyService));

The idea being that i should be able to freeze my test ILog test double using:

var log = fixture.Freeze<Mock<ILog>>;

and verify it was called later after the main method invocation by:

log.Verify(l => l.Warn, Times.Once);

However, when i call fixture.Create<MyService> AutoFixture does not replace the Logger property with a mock of ILog. I have also tried removing the default value LogManager.GetLogger<etc> in which case the value of ILog is null.

Other properties are correctly populated with test doubles but not this one.

For reference, the ILog interface is from ServiceStack's logging framework and looks like this:

public interface ILog
{
    bool IsDebugEnabled { get; }
    void Debug(object message);
    void Debug(object message, Exception exception);
    void DebugFormat(string format, params object[] args);
    void Error(object message);
    void Error(object message, Exception exception);
    void ErrorFormat(string format, params object[] args);
    void Fatal(object message);
    void Fatal(object message, Exception exception);
    void FatalFormat(string format, params object[] args);
    void Info(object message);
    void Info(object message, Exception exception);
    void InfoFormat(string format, params object[] args);
    void Warn(object message);
    void Warn(object message, Exception exception);
    void WarnFormat(string format, params object[] args);
}

I have also verified that creating the Mocks manually and setting them up with Moq works - the ILog property is correctly replaced with my Mock using:

myServiceMock.Setup(s => s.Logger).Returns(myLoggerMock)

Can anyone shed any light on this?

Steps to Reproduce

Test

    using ServiceStack;
    using ServiceStack.Logging;
    using ServiceStack.Web;
    using MyApp;

    [Test]
    public void LogTest()
    {
        var fixture = new Fixture().Customize(new AutoMoqCustomization());

        var log = fixture.Freeze<Mock<ILog>>();
        var request = fixture.Freeze<Mock<IRequest>>();
        var response = new Mock<IResponse>();
        var service = fixture.Create<MyService>();

        request.Setup(r => r.Response).Returns(response.Object);

        service.Post(null);

        log.Verify(l => l.Warn(It.IsAny<string>()), Times.Once());
    }

Service Class - NB: Normally the Logger property would be suffixed = LogManager.GetLogger(typeof(MyService)) but i have omitted it at this point to see the issue.

using ServiceStack;
using ServiceStack.Logging;

namespace MyApp
{
  public class MyService : BaseService
  {
    public override ILog Logger { get; }

    public MyResponse Post(MyRequest request)
    {
        if (request != null) return new MyResponse() {Message = request.Message};

        Logger.Warn("Null request object");
        return null;
    }
   }

public abstract class BaseService : Service
{
    public abstract ILog Logger { get; }
}

public class MyRequest
{
    public string Message { get; set; }
}

public class MyResponse
{
    public string Message { get; set; }
}
}

If you breakpoint on the service.Post(null) line you will see the ILog property is still null but other properties have mocks.

Forgotten answered 15/10, 2015 at 16:36 Comment(9)
Is the class with the Logger property a concrete class? AutoMoq doesn't proxy concrete classes: blog.ploeh.dk/2010/08/25/…Podolsk
Hi Mark, it is a concrete class so I take your point but if that's the case, why are all the other properties on the same class populated with mocks automatically by AutoMoq?Forgotten
I don't know, since you're not showing me a repro...Podolsk
Have added repro steps.Forgotten
What do you mean by other properties? As given here, MyService has no other properties, but on the other hand, the Service class isn't described. Please provide a Short, Self Contained, Correct (Compilable), Example.Podolsk
My apologies, I made the false assumption that the using statements indicated that the Service class was from ServiceStack. The class definition can be seen here: github.com/ServiceStack/ServiceStack/blob/master/src/…Forgotten
What version of Moq are you using? You might be experiencing this bug.Ledaledah
Those properties have a lot of ambient behaviour in them. Is this really an AutoFixture question, or is it rather a ServiceStack question? Can the behaviour that causes trouble be reproduced without ServiceStack?Podolsk
I haven't been able to reproduce it on my own POCO's so you're probably right in that it's more a ServiceStack question. However, as i mentioned to @Enrico, i suspect it would be more prudent to refactor my code and test outside the ServiceStack class.Forgotten
L
7

All the AutoMoqCustomization does is configure AutoFixture to delegate all requests for interfaces or abstract types to Moq. It doesn't automatically setup any stubs for the properties and methods of the Test Doubles that get created.

However, as of AutoFixture 3.20.0 there is a new auto-mocking customization that does exactly that – the AutoConfiguredMoqCustomization:

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());

Enabling that customization makes sure that the Test Doubles that come out of Moq are configured to return objects created by AutoFixture from all their public properties.

But there's a problem. As reported by @dcastro, a bug was introduced in Moq 4.2.1502.911 that causes read-only properties – like the MyService.Logger property in your case – to be overridden by Moq after AutoFixture has configured them.

So, even if you switch to the AutoConfiguredMoqCustomization the Logger property would still be null by virtue of it being read-only. Other properties and methods with return values, on the other hand, will be configured to return objects created by AutoFixture.

Your best option right now is to start using the AutoConfiguredMoqCustomization and downgrade to an earlier version of Moq where read-only properties are still being configured correctly.

Ledaledah answered 16/10, 2015 at 8:49 Comment(2)
Thanks for the input Enrico, i followed your suggestion and downgraded but still get the same response (i.e. a null value in the Logger property). However, these issues have made me re-consider the way my code is structure. As @mark-seemann points out in his blog, there's probably something fundamentally wrong with my code structure if i'm having to setup a ton of mocks to test something. It doesn't alter the fact that I still cant get it to setup a test double for that particular property for some reason.Forgotten
Marked Enrico's answer as correct, although it doesn't solve the problem it points out an issue which may be the cause.Forgotten

© 2022 - 2024 — McMap. All rights reserved.