在与 Asp.Net Core TestServer 的集成测试中设置虚拟 IP 地址

Posted

技术标签:

【中文标题】在与 Asp.Net Core TestServer 的集成测试中设置虚拟 IP 地址【英文标题】:Set dummy IP address in integration test with Asp.Net Core TestServer 【发布时间】:2018-08-21 00:10:51 【问题描述】:

我有一个 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 用于向 Web API(被测系统)发送 HTTP 请求并检索响应。

在实际测试系统中,有一些组件会从每个请求中提取发送者 IP 地址,如下所示:

HttpContext httpContext = ReceiveHttpContextDuringAuthentication();

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

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

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

【问题讨论】:

显示ReceiveHttpContextDuringAuthentication() 只是一个虚构的函数来表示正在发生的事情。在实际代码中,IP 地址是在身份验证框架 (ASOS) 提供的扩展点中提取的。该框架将当前请求 HttpContext 作为输入之一传递给扩展点。 【参考方案1】:

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

protected override IHostBuilder CreateHostBuilder()

    return Host
        .CreateDefaultBuilder()
        .ConfigureWebHostDefaults(builder =>
        
            builder.UseStartup<Startup>();
        )
        .ConfigureServices(services =>
        
            services.AddSingleton<IStartupFilter, CustomStartupFilter>();
        );

【讨论】:

【参考方案2】:

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

除非绝对必要,否则无需为了测试而更改产品代码。

添加您自己的中间件而不覆盖 Startup 类方法的最简单方法是按照 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>();
            );
        
    

【讨论】:

感谢您花时间回答,即使它相对较旧。目前我离类似的项目还很远,让我们看看新项目会发生什么。【参考方案3】:

根据这个答案in ASP.NET Core, is there any way to set up middleware from Program.cs?

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

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);
        ;
    

【讨论】:

非常感谢您的回答。如果不继承新的 Startup 类(它有其自身的缺点),直接通过WebApplicationFactory 配置额外的中间件是不可能的,但这种方法非常有效。 对于使用 .NET 5 或 Core 3.x 的人,应将 .ConfigureServices 调用放入 protected override IHost CreateHost(IHostBuilder builder) 而不是 CreateWebHostBuilder 在 ASP.NET Core 3.x 或更高版本中使用这种方法?见this answer。【参考方案4】:

您可以编写中间件来设置自定义 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);
    

然后你可以像这样创建StartupStub 类:

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>());

【讨论】:

看起来这可能是缺少的部分。谢谢,例如,我会尝试,但是是的,它看起来很有希望 您是否也使用 dotnet core 2.x 进行了这项工作?看起来“配置”在 2.x 中不能再被覆盖了。 @Ralf 你在你的Startup 班级中将Configure 标记为virtual 吗? @PavelAgarkov 您的解决方案是在中间件中硬编码 IP,这意味着相同的 IP 地址将用于所有集成测试。是否还有一种方法可以为每个测试用例声明要单独使用的 IP(例如,在创建 HttpRequest 时),以便可以考虑各种测试场景(具有各种 IP 地址)? @Dario,当然,您可以使用 ConfigureTestServices 注册一些服务,这些服务将被注入 FakeRemoteIpAddressMiddleware 并用于获取当前 IP 地址。这样您就可以在使用客户端之前指定不同的 IP 地址。

以上是关于在与 Asp.Net Core TestServer 的集成测试中设置虚拟 IP 地址的主要内容,如果未能解决你的问题,请参考以下文章

在与当前用户不同的用户下运行 ASP .NET Web 应用程序

ASP.NET Core (.NET Core) and ASP.NET Core (.NET Framework)区别

Asp.Net core (Full .Net framework) vs Asp.Net core (.Net Core) 性能

Asp.NET Core进阶 第四篇 Asp.Net Core Blazor框架

.NET Core 1.0ASP.NET Core 1.0和EF Core 1.0简介

asp.net core 注入后仍然报错?