简体   繁体   中英

running asp.net web api application in integration tests

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM