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.

Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
RNDThoughts
  • 892
  • 3
  • 13
  • 30
  • Is the class with the `Logger` property a concrete class? AutoMoq doesn't proxy concrete classes: http://blog.ploeh.dk/2010/08/25/ChangingthebehaviorofAutoFixtureauto-mockingwithMoq – Mark Seemann Oct 15 '15 at 19:03
  • 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? – RNDThoughts Oct 15 '15 at 19:06
  • 1
    I don't know, since you're not showing me a repro... – Mark Seemann Oct 15 '15 at 19:18
  • Have added repro steps. – RNDThoughts Oct 15 '15 at 20:32
  • 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](http://sscce.org). – Mark Seemann Oct 16 '15 at 05:40
  • 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: https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack/Service.cs – RNDThoughts Oct 16 '15 at 07:53
  • What version of Moq are you using? You might be experiencing [this bug](https://github.com/AutoFixture/AutoFixture/issues/434). – Enrico Campidoglio Oct 16 '15 at 09:05
  • 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? – Mark Seemann Oct 16 '15 at 10:10
  • 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. – RNDThoughts Oct 16 '15 at 10:20

1 Answers1

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.

Community
  • 1
  • 1
Enrico Campidoglio
  • 56,676
  • 12
  • 126
  • 154
  • 1
    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. – RNDThoughts Oct 16 '15 at 09:51
  • Marked Enrico's answer as correct, although it doesn't solve the problem it points out an issue which may be the cause. – RNDThoughts Oct 21 '15 at 16:25