1

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.


Now here is the problem:

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

1 Answer 1

1

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));
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.