Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture AutoMoq not creating mocks for some properties

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.

like image 689
RNDThoughts Avatar asked Oct 15 '15 16:10

RNDThoughts


1 Answers

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.

like image 140
Enrico Campidoglio Avatar answered Nov 15 '22 04:11

Enrico Campidoglio