AutoFixture AutoMoq 不为某些属性创建模拟
本文关键字:属性 创建 模拟 AutoMoq AutoFixture | 更新日期: 2023-09-27 18:18:03
我正在使用 AutoFixture 和 AutoMoqCustomization
并尝试创建一个包含只读属性的类的实例,因此:
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);
}
我还验证了手动创建模拟并使用 Moq 设置它们的工作原理 - ILog
属性被正确替换为我的模拟:
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
属性仍为 null,但其他属性具有模拟。
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 覆盖。
因此,即使您切换到 AutoConfiguredMoqCustomization
,Logger
属性仍然是 null,因为它是只读的。另一方面,具有返回值的其他属性和方法将配置为返回由 AutoFixture 创建的对象。
您现在最好的选择是开始使用 AutoConfiguredMoqCustomization
并降级到早期版本的 Moq,其中只读属性仍被正确配置。