5

我正在使用 AutoFixtureAutoMoqCustomization并尝试创建一个包含只读属性的类的实例,因此:

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

这个想法是我应该能够ILog使用以下方法冻结我的测试测试:

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

并验证它是在 main 方法调用之后通过以下方式调用的:

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

但是,当我调用fixture.Create<MyService>AutoFixture 时,不会Logger用模拟的ILog. 我还尝试删除默认值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);
}

我还验证了手动创建 Mocks 并使用 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());
    }

服务类- 注意:通常该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属性仍然为空,但其他属性有模拟。

4

1 回答 1

7

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

但是,从AutoFixture 3.20.0 开始,有一个新的自动模拟自定义可以做到这一点—— AutoConfiguredMoqCustomization

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

启用该自定义可确保来自 Moq 的测试替身配置为从其所有公共属性返回由 AutoFixture 创建的对象。

但是有一个问题。正如@dcastro 所报告的那样, Moq 4.2.1502.911 中引入了一个错误,该错误导致只读属性(如MyService.Logger您的情况下的属性)在 AutoFixture 配置它们后被 Moq 覆盖。

因此,即使您切换到AutoConfiguredMoqCustomizationLogger属性,由于它是只读的,它仍然会为空。另一方面,其他具有返回值的属性和方法将被配置为返回由 AutoFixture 创建的对象。

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

于 2015-10-16T08:49:12.857 回答