You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:30:48