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 覆盖。
所以,即使你切换到 AutoConfiguredMoqCustomization
,Logger
属性 也会 仍然是 null 因为它被读取了-只要。另一方面,具有 return 值的其他属性和方法将被配置为由 AutoFixture 创建的 return 个对象。
您现在最好的选择是开始使用 AutoConfiguredMoqCustomization
并降级到 earlier version of Moq,其中只读属性仍被正确配置。
我将 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 覆盖。
所以,即使你切换到 AutoConfiguredMoqCustomization
,Logger
属性 也会 仍然是 null 因为它被读取了-只要。另一方面,具有 return 值的其他属性和方法将被配置为由 AutoFixture 创建的 return 个对象。
您现在最好的选择是开始使用 AutoConfiguredMoqCustomization
并降级到 earlier version of Moq,其中只读属性仍被正确配置。