使用AutoData特性后AutoFixture无法创建接口实例的原因
Freeze<Mock<T>>的工作逻辑
调用_fixture.Freeze<Mock<IConversationStorage>>()时,AutoFixture会完成两个关键操作:一是创建Mock<IConversationStorage>实例并“冻结”(后续请求该类型会返回同一个实例);二是自动把这个Mock的Object属性(也就是IConversationStorage的Mock实现)注册为Fixture中IConversationStorage类型的默认实例。所以后续测试里需要IConversationStorage时,Fixture直接返回Mock的实现,不会去尝试创建接口的真实实现,自然不会报错。直接用
[AutoData]注入Mock<T>参数的问题
当你把Mock<IConversationStorage>、Mock<ISummariesMetadataGenerator>作为测试方法参数时,AutoFixture确实能创建这些Mock实例,但你的被测类ConversationMetadataGenerator依赖的是IConversationStorage接口,不是Mock<IConversationStorage>类型。此时AutoFixture创建ConversationMetadataGenerator时,需要注入IConversationStorage,但它默认不会自动把Mock<T>的Object和T类型关联起来,所以会尝试去创建IConversationStorage的实例——而接口没有默认实现,就会抛出无法创建的错误。
方法一:用AutoMoqData特性(推荐)
先安装AutoFixture.AutoMoq NuGet包,然后自定义一个特性:
public class AutoMoqDataAttribute : AutoDataAttribute { public AutoMoqDataAttribute() : base(() => new Fixture().Customize(new AutoMoqCustomization())) { } }
测试方法改用[AutoMoqData]特性,此时AutoFixture会自动为所有接口创建对应的Mock,并且把Mock的Object注册为接口的实现。不管你是注入Mock<IConversationStorage>(用来设置Mock行为)还是直接注入IConversationStorage,都能正常工作:
[Theory, AutoMoqData] public void YourTest( Mock<IConversationStorage> storageMock, Mock<ISummariesMetadataGenerator> generatorMock, ConversationMetadataGenerator sut) { // 用storageMock.Setup(...)设置预期行为 // sut会自动注入storageMock.Object作为IConversationStorage实例 }
方法二:手动关联Mock与接口
如果不想用AutoMoq,也可以在自定义的AutoData特性里手动处理:
public class CustomAutoDataAttribute : AutoDataAttribute { public CustomAutoDataAttribute() : base(() => { var fixture = new Fixture(); // 创建并冻结Mock,同时注册其Object为接口的实现 var storageMock = fixture.Freeze<Mock<IConversationStorage>>(); fixture.Register<IConversationStorage>(() => storageMock.Object); var generatorMock = fixture.Freeze<Mock<ISummariesMetadataGenerator>>(); fixture.Register<ISummariesMetadataGenerator>(() => generatorMock.Object); return fixture; }) { } }
测试方法使用[CustomAutoData]特性,参数注入Mock和被测类即可正常运行。
内容的提问来源于stack exchange,提问作者Piotrek

