superjos
superjos

Reputation: 12705

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

I have a C# Asp.Net Core (1.x) project, implementing a web REST API, and its related integration test project, where before any test there's a setup similar to:

// ...

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

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

HttpClient client = server.CreateClient();

// ...

During tests, the client is used to send HTTP requests to web API (the system under test) and retrieve responses.

Within actual system under test there's some component extracting sender IP address from each request, as in:

HttpContext httpContext = ReceiveHttpContextDuringAuthentication();

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

Now during integration tests this bit of code fails to find an IP address, as RemoteIpAddress is always null.

Is there a way to set that to some known value from within test code? I searched here on SO but could not find anything similar. TA

Upvotes: 33

Views: 7163

Answers (5)

Michael Liu
Michael Liu

Reputation: 55419

Does your Web app call IApplicationBuilder.UseForwardedHeaders?

app.UseForwardedHeaders(new() {
    ForwardedHeaders = ForwardedHeaders.XForwardedFor
});

If so, then you can simply add the X-Forwarded-For header to your test HTTP requests:

public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
    protected override void ConfigureClient(HttpClient client)
    {
        base.ConfigureClient(client);
        client.DefaultRequestHeaders.Add("X-Forwarded-For", "192.168.0.1");
    }
}

No need to write any test-only middleware!

Upvotes: 4

Lars Michael
Lars Michael

Reputation: 693

I used Elliott's answer within an ASP.NET Core 2.2 project. However, updating to ASP.NET 5.0, I had to replace the override of CreateWebHostBuilder with the below override of CreateHostBuilder:

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

Upvotes: 1

Jl Nando
Jl Nando

Reputation: 21

Using WebHost.CreateDefaultBuilder can mess up with your app configuration.

And there's no need to change Product code just to accommodate for testing, unless absolutely necessary.

The simplest way to add your own middleware, without overriding Startup class methods, is to add the middleware through a IStartupFilter‍ as suggested by Elliott's answer.

But instead of using WebHost.CreateDefaultBuilder, just use

base.CreateWebHostBuilder().ConfigureServices...

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

Upvotes: 2

Elliott
Elliott

Reputation: 2729

As per this answer in ASP.NET Core, is there any way to set up middleware from Program.cs?

It's also possible to configure the middleware from ConfigureServices, which allows you to create a custom WebApplicationFactory without the need for a 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);
        };
    }
}

Upvotes: 23

Pavel Agarkov
Pavel Agarkov

Reputation: 3783

You can write middleware to set custom IP Address since this property is writable:

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

Then you can create StartupStub class like this:

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

And use it to create a TestServer:

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

Upvotes: 37

Related Questions