AutoFixture AutoMoq 不为某些属性创建模拟

AutoFixture AutoMoq not creating mocks for some properties

我将 AutoFixture 与 AutoMoqCustomization 一起使用,并尝试创建一个 class 的实例,其中包含只读 属性 因此:

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

我的想法是我应该能够冻结我的测试ILog测试双重使用:

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

并通过以下方式验证它在主方法调用之后被调用:

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

但是,当我调用 fixture.Create<MyService> 时,AutoFixture 不会用 ILog 的模拟替换 Logger 属性。我也尝试删除默认值 LogManager.GetLogger<etc> 在这种情况下 ILog 的值是 null.

其他属性正确填充了测试替身,但不是这个。

作为参考,ILog 接口来自 ServiceStack 的日志框架,如下所示:

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);
}

我还验证了手动创建 Mock 并使用 Moq 进行设置是可行的 - ILog 属性 已正确替换为我的 Mock 使用:

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

任何人都可以阐明这一点吗?

重现步骤

测试

    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 - 注意:通常 Logger 属性 会带有后缀 = LogManager.GetLogger(typeof(MyService)) 但我在这点看问题。

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; }
}
}

如果您在 service.Post(null) 行上设置断点,您将看到 ILog 属性 仍然为空,但其他属性具有模拟。

AutoMoqCustomization 所做的只是配置 AutoFixture 以将对接口或抽象类型的所有请求委托给 Moq。它不会自动为创建的测试替身的属性和方法设置任何存根。

然而,从 AutoFixture 3.20.0 there is a new auto-mocking customization 开始,它确实做到了这一点 – AutoConfiguredMoqCustomization:

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

启用该自定义可确保将来自 Moq 的测试替身配置为 return 由 AutoFixture 根据其所有 public 属性创建的对象。

但是有一个问题。作为 reported by @dcastro, a bug was introduced in Moq 4.2.1502.911 导致 只读属性 - 就像你的情况下的 MyService.Logger 属性 - 在 AutoFixture 配置它们之后被 Moq 覆盖。

所以,即使你切换到 AutoConfiguredMoqCustomizationLogger 属性 也会 仍然是 null 因为它被读取了-只要。另一方面,具有 return 值的其他属性和方法将被配置为由 AutoFixture 创建的 return 个对象。

您现在最好的选择是开始使用 AutoConfiguredMoqCustomization 并降级到 earlier version of Moq,其中只读属性仍被正确配置。