【问题标题】:Set dummy IP address in integration test with Asp.Net Core TestServer在与 Asp.Net Core TestServer 的集成测试中设置虚拟 IP 地址
【发布时间】: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 作为输入之一传递给扩展点。

标签: asp.net-core integration-testing asp.net-core-webapi


【解决方案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 地址。
    猜你喜欢
    • 1970-01-01
    • 2010-09-09
    • 2020-10-27
    • 1970-01-01
    • 2012-11-06
    • 2020-03-04
    • 1970-01-01
    • 2015-05-14
    • 2016-07-25
    相关资源
    最近更新 更多