使用 Asp.Net 核心测试服务器在集成测试中设置虚拟 IP 地址

Set dummy IP address in integration test with Asp.Net Core TestServer

我有一个 C# Asp.Net Core (1.x) 项目,实现了一个 web REST API,及其相关的集成测试项目,在任何测试之前都有一个类似于:

// ...

IWebHostBuilder webHostBuilder = GetWebHostBuilderSimilarToRealOne()
    .UseStartup<MyTestStartup>();

TestServer server = new TestServer(webHostBuilder);
server.BaseAddress = new Uri("http://localhost:5000");

HttpClient client = server.CreateClient();

// ...

在测试期间,client 用于向网络 API(被测系统)发送 HTTP 请求并检索响应。

在实际被测系统中,有一些组件从每个请求中提取发件人 IP 地址,如:

HttpContext httpContext = ReceiveHttpContextDuringAuthentication();

// edge cases omitted for brevity
string remoteIpAddress = httpContext?.Connection?.RemoteIpAddress?.ToString()

现在在集成测试期间,这段代码无法找到 IP 地址,因为 RemoteIpAddress 始终为空。

有没有办法从测试代码中将其设置为某个已知值?我在这里搜索了 SO 但找不到类似的东西。 TA

您可以编写中间件来设置自定义 IP 地址,因为这个 属性 是可写的:

public class FakeRemoteIpAddressMiddleware
{
    private readonly RequestDelegate next;
    private readonly IPAddress fakeIpAddress = IPAddress.Parse("127.168.1.32");

    public FakeRemoteIpAddressMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        httpContext.Connection.RemoteIpAddress = fakeIpAddress;

        await this.next(httpContext);
    }
}

然后你可以这样创建StartupStubclass:

public class StartupStub : Startup
{
    public StartupStub(IConfiguration configuration) : base(configuration)
    {
    }

    public override void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMiddleware<FakeRemoteIpAddressMiddleware>();
        base.Configure(app, env);
    }
}

并用它来创建 TestServer

new TestServer(new WebHostBuilder().UseStartup<StartupStub>());

根据这个回答

也可以从 ConfigureServices 配置中间件,它允许您创建自定义 WebApplicationFactory 而无需 StartupStub class:

public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
    protected override IWebHostBuilder CreateWebHostBuilder()
    {
        return WebHost
            .CreateDefaultBuilder<Startup>(new string[0])
            .ConfigureServices(services =>
            {
                services.AddSingleton<IStartupFilter, CustomStartupFilter>();
            });
    }
}


public class CustomStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return app =>
        {
            app.UseMiddleware<FakeRemoteIpAddressMiddleware>();
            next(app);
        };
    }
}

使用 WebHost.CreateDefaultBuilder 可能会扰乱您的应用配置。

除非绝对必要,否则无需更改产品代码以适应测试。

在不覆盖 Startup class 方法的情况下添加自己的中间件的最简单方法是按照 Elliott 的回答所建议的那样通过 IStartupFilter‍ 添加中间件。

但不要使用 WebHost.CreateDefaultBuilder,只需使用

base.CreateWebHostBuilder().ConfigureServices...

    public class CustomWAF : WebApplicationFactory<Startup>
    {
        protected override IWebHostBuilder CreateWebHostBuilder()
        {
            return base.CreateWebHostBuilder().ConfigureServices(services =>
            {
                services.AddSingleton<IStartupFilter, CustomStartupFilter>();
            });
        }
    }

我在 ASP.NET Core 2.2 项目中使用了 。但是,更新到 ASP.NET 5.0,我不得不将 CreateWebHostBuilder 的覆盖替换为以下 CreateHostBuilder 的覆盖:

protected override IHostBuilder CreateHostBuilder()
{
    return Host
        .CreateDefaultBuilder()
        .ConfigureWebHostDefaults(builder =>
        {
            builder.UseStartup<Startup>();
        })
        .ConfigureServices(services =>
        {
            services.AddSingleton<IStartupFilter, CustomStartupFilter>();
        });
}