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

如何在测试中用Options模式以实例替换应用配置?

如何在测试中直接传入Options实例配置ASP.NET DI容器

我使用C# Options模式定义了如下配置类型:

public class MySettings
{
    public bool IsEnabled { get; set; }

    public int NumberOfDays { get; set; }
}

在测试场景中,我希望通过CustomWebApplicationFactory配置DI容器,让它始终提供同一个MySettings实例。最初尝试直接传入实例但无法编译(因为Configure<T>不接受T类型的实例参数):

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    private readonly MySettings _settings;

    public CustomWebApplicationFactory(MySettings settings)
        : base()
     => _settings = settings;

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);

        // 编译失败:Configure<T>不接受T实例
        builder.ConfigureServices(services => services.Configure<MySettings>(_settings));
    }
}

目前我通过传入Action<MySettings>的方式解决,但希望能直接传入实例提升测试代码的可读性:

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    private readonly Action<MySettings> _configureSettings;

    public CustomWebApplicationFactory(Action<MySettings> configureSettings)
        : base()
     => _configureSettings = configureSettings;

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);

        builder.ConfigureServices(services => services.Configure<MySettings>(_configureSettings));
    }
}

解决方案

有两种简洁的方式实现直接传入实例的需求:

方法1:封装扩展方法实现实例配置

通过扩展方法封装属性复制逻辑,让Configure<T>支持直接接收实例:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection Configure<T>(this IServiceCollection services, T settings) 
        where T : class, new()
    {
        return services.Configure<T>(opt =>
        {
            // 反射复制所有公共实例属性值
            var properties = typeof(T).GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
            foreach (var prop in properties)
            {
                prop.SetValue(opt, prop.GetValue(settings));
            }
        });
    }
}

修改CustomWebApplicationFactory即可直接传入实例:

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    private readonly MySettings _settings;

    public CustomWebApplicationFactory(MySettings settings)
        : base()
     => _settings = settings;

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);

        // 现在可以直接传入实例
        builder.ConfigureServices(services => services.Configure(_settings));
    }
}

方法2:直接注册IOptions<MySettings>实例

如果不需要Options的配置管道(比如验证、后期配置),可以直接替换DI中IOptions<MySettings>的注册:

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    private readonly MySettings _settings;

    public CustomWebApplicationFactory(MySettings settings)
        : base()
     => _settings = settings;

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);

        builder.ConfigureServices(services =>
        {
            // 移除原有IOptions注册(避免冲突)
            var existingOptions = services.SingleOrDefault(d => d.ServiceType == typeof(IOptions<MySettings>));
            if (existingOptions != null)
            {
                services.Remove(existingOptions);
            }

            // 注册我们的实例包装为IOptions
            services.AddSingleton(Options.Create(_settings));
        });
    }
}

测试代码示例

修改后,测试代码可以直接创建实例传入,可读性大幅提升:

// 直接构造测试用配置实例
var testSettings = new MySettings 
{ 
    IsEnabled = true, 
    NumberOfDays = 7 
};

// 传入实例创建工厂
var factory = new CustomWebApplicationFactory(testSettings);

// 使用工厂创建测试客户端
using var client = factory.CreateClient();
// ... 执行测试逻辑

内容的提问来源于stack exchange,提问作者mu88

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 00:25:28