使用xunit、Autofixture、NSubstitute时Freeze间接依赖为何失败?
问题原因与解决方案
为什么测试会失败?
AutoFixture默认行为是为每个依赖项生成全新实例——哪怕你用Freeze<IServiceA>()固定了一个IServiceA实例,当AutoFixture自动创建IServiceB时,它会重新生成一个未配置的IServiceA注入进去,而非复用你冻结的那个。最终IServiceB实际调用的是无返回值配置的IServiceA,自然导致测试结果不符合预期。
无需手动声明IServiceB的解决办法
借助AutoFixture的AutoConfiguredNSubstituteCustomization就能解决问题,它会让NSubstitute的模拟对象自动复用容器中已冻结的依赖实例,完全不用手动配置中间的IServiceB。
具体代码示例
using Xunit; using AutoFixture; using AutoFixture.AutoNSubstitute; using NSubstitute; public class MyClassTests { [Fact] public void MyClass_UsesConfiguredServiceA() { // 用AutoConfiguredNSubstituteCustomization替代普通AutoNSubstituteCustomization var fixture = new Fixture().Customize(new AutoConfiguredNSubstituteCustomization()); // 冻结IServiceA并配置返回值 var serviceA = fixture.Freeze<IServiceA>(); serviceA.MethodInA().Returns("Hello"); // AutoFixture自动创建IServiceB,并注入已冻结的serviceA var myClass = fixture.Create<MyClass>(); // 执行测试并断言 var result = myClass.DoSomething(); Assert.Equal("Hello", result); } }
原理说明
AutoConfiguredNSubstituteCustomization会告诉AutoFixture:创建NSubstitute模拟对象(比如IServiceB)时,优先从Fixture容器中查找已存在的依赖实例(即你冻结的IServiceA)作为构造函数参数,而非每次生成新实例。这样整个依赖链会自动复用你配置好的冻结对象,完全无需手动声明中间依赖。
如果不想用上述自定义配置,也可以手动指定IServiceB的创建逻辑,强制注入冻结的IServiceA:
fixture.Customize<IServiceB>(c => c.FromFactory(() => Substitute.For<IServiceB>(fixture.Create<IServiceA>())));
不过这种方式不如前者简洁,推荐优先使用AutoConfiguredNSubstituteCustomization。
内容的提问来源于stack exchange,提问作者Leyu
相关产品推荐
相关产品推荐

