Reputation: 194
I have configured an xUnit project to test an Identity Server implementation. I have created a TestStartup
class which inherits Startup
and overrides my Identity Server implementation for testing purposes. The problem is when I call my TestStartup
using my custom WebApplicationFactory
, my endpoints are not mapped. If I call Startup
from my custom WebApplicationFactory
, my endpoints are mapped.
Startup.cs
protected readonly IConfiguration _configuration;
protected readonly IWebHostEnvironment _webHostEnvironment;
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
_configuration = configuration;
_webHostEnvironment = environment;
}
public virtual void ConfigureServices(IServiceCollection services)
{
///code goes here
ConfigureIdentityServices(services);
}
/// <summary>
/// Split into its own method so we can override for testing
/// </summary>
/// <param name="services"></param>
public virtual void ConfigureIdentityServices(IServiceCollection services)
{
///code goes here
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseRouting();
//app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
TestStartup.cs
public TestStartup(IConfiguration configuration, IWebHostEnvironment environment) : base(configuration, environment)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
}
/// <summary>
/// In memory identity database implementation for testing
/// </summary>
/// <param name="services"></param>
public override void ConfigureIdentityServices(IServiceCollection services)
{
//test implementation
}
public override void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
base.Configure(app, env);
}
Custom WebApplicationFactory
public class ApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder(null).UseEnvironment("Development")
.UseStartup<TStartup>();
}
}
Controller Test Class
public class UserControllerTests : IClassFixture<ApplicationFactory<Startup>>
{
private readonly ApplicationFactory<Startup> _factory;
public UserControllerTests(ApplicationFactory<Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_Users()
{
var client = _factory.CreateClient();
var result = await client.GetAsync("/user/users");
result.StatusCode.Should().Be(HttpStatusCode.OK);
}
}
When I run my controller test, if inject TestStartup
instead of Startup
, I don't get any endpoints returned from the endpoint routing, even though I call the base.Configure(app,env)
method from my TestStartup
class.
Upvotes: 4
Views: 2327
Reputation: 136
I have the exact same issue. Using a subclassed startup in my custom WebApplicationFactory subclass results in no endpoints being registered whereas UseStartup regsiters them. Odd thing is that I have another API project (without Identity) where using the subclassed startup actually registers endpoints. Tried to comment everything out in startup, except services.AddControllers and app.UseRouting/app.UseEndpoints in the Identity project and it still doesn't work.
Edit: Found the solution. Apparently services.AddControllers only registers routes in the executing assembly, which in the subclassed integrationtest scenario is the test assembly. By adding the required controllers as application parts, the routing system picks up the routes:
services.AddControllers()
.AddApplicationPart(typeof(<controllername>).Assembly);
things will work.
Upvotes: 12