使用 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);
}
}
然后你可以这样创建StartupStub
class:
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>();
});
}
我有一个 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);
}
}
然后你可以这样创建StartupStub
class:
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 项目中使用了 CreateWebHostBuilder
的覆盖替换为以下 CreateHostBuilder
的覆盖:
protected override IHostBuilder CreateHostBuilder()
{
return Host
.CreateDefaultBuilder()
.ConfigureWebHostDefaults(builder =>
{
builder.UseStartup<Startup>();
})
.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter, CustomStartupFilter>();
});
}