Reputation: 36058
I have a asp.net core web api application that runs great like this:
class Program
{
/// <summary>
/// Main method
/// </summary>
static void Main(string[] args)
{
// pass this as a parameter to specify what database I will like to use
Func<IServiceProvider, IMyDatabase> GetDatabaseFactory = provider =>
{
// used for testing purposes. In production I will use the real DB
return new MyDummyDatabase();
}
// create on a method so that it can be unit tested
WebApplication app = CreateMyAppWebApiApplication(GetDatabaseFactory);
// run application
app.Run();
}
}
And here is the method CreateMyAppWebApiApplication.
/* I removed a lot of stuff I just want to illustrate the idea. Moreover, I have hardocoded a lot of stuff for testing purposes. Once it works I will move it to a configuration file.*/
static WebApplication CreateMyAppWebApiApplication(StartAspDotNetParameters parameters)
{
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(k =>
{
var port = 8888;
k.Listen(System.Net.IPAddress.Any, port, listenOptions =>
{
// Enable support for HTTP1 and HTTP2 (required if you want to host gRPC endpoints)
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps();
});
});
#region Add IoC dependencies
// .. code some dependencies I need for the controlers
#endregion
// add controllers
var mvcBuilder = builder.Services.AddControllers();
// serialize enums as string
mvcBuilder.AddJsonOptions(opts =>
opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter())
);
// Configure Swagger/OpenAPI. More info: https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
// code to configure...
});
WebApplication? app = builder.Build();
#region Configure the HTTP request pipeline.
// first middleware to intercept swagger.json file
// I have hardocded the path for testing purposes
app.Use(async (HttpContext context, Func<Task> next) =>
{
if (requestUrl.EndsWith("myapp-swagger.json"))
{
var content = File.ReadAllText(@"T:\repos\.....\myapp-swagger.json.json");
context.Response.ContentLength = content.Length;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(content);
return;
}
else
{
// else execute next middleware
await next();
}
});
// enable swagger
app.UseSwagger();
// change swager endpoint
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "documentation";
c.SwaggerEndpoint("/myapp-swagger.json", "MY API");
});
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// This will run the application
//// execute endpoint
//app.Run();
return app;
#endregion
}
The things important to note about this method are:
// I changed swagger default endpoint
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "documentation";
c.SwaggerEndpoint("/myapp-swagger.json", "MY API");
});
// AND
// first middleware to intercept swagger.json file
// I have hardocded the path for testing purposes
app.Use(async (HttpContext context, Func<Task> next) =>
{
if (requestUrl.EndsWith("myapp-swagger.json"))
{
var content = File.ReadAllText(@"T:\repos\.....\myapp-swagger.json.json");
context.Response.ContentLength = content.Length;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(content);
return;
}
else
{
// else execute next middleware
await next();
}
});
Anyways that code works great.
When I try to run that same code from a Tests project like this:
[Fact]
public async Task TestUserPermissions_IntegrationTest()
{
// pass the same dummyDatabase
WebApplication app = CreateMyAppWebApiApplication(provider =>
{
// used for testing purposes. In production I will use the real DB
return new MyDummyDatabase();
});
loginWorked = false;
var taskLogin = Task.Run(async () =>
{
// make sure app starts by waiting 5 seconds
await Task.Delay(5000);
using var client = new HttpClient();
var json = @"{ 'username':'tono', 'password':'myPassword'}".Replace("'", "\"");
var content = new StringContent(json, Encoding.UTF8, "application/json");
var result = await client.PostAsync("https://localhost:8888/api/LoginController/Login", content);
Console.WriteLine(result.StatusCode);
loginWorked = result.StatusCode == 200;
});
// run application
app.Run();
await taskLogin ;
Assert.True(loginWorked);
}
The app runs but I am not able to consume the API when running in the Test project
Upvotes: 1
Views: 979
Reputation: 36058
Finally found the answer. The controllers where not being found because I was running the project from a different assembly. This solution for stackoverflow made my Test pass:
https://stackoverflow.com/a/59121354/637142
In other words I ended up adding this code
// add controllers
var mvcBuilder = builder.Services.AddControllers();
// add controllers from this assembly. This is needed in case we are calling this method from unit tests project.
mvcBuilder.PartManager.ApplicationParts.Add(new AssemblyPart(typeof(MyCustomController).Assembly));
Upvotes: 1