Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.Net Core Integration TestServer with Generic IHostBuilder

I've updated my website with .Net Core 3.0 preview 2 and I want to make Integration Test with a TestServer. In .Net Core 2.2, I've been able to make it using WebApplicationFactory<Startup>

Since WebHostBuilder is about to be deprecated (see (https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-2.2&tabs=visual-studio) for more details.), I want to follow up and implement the new Generic HostBuilder now. It is working great for launching the website but it crashes when I launch my Integration tests. I know that WebApplicationFactory does use WebHostBuilder and that's why it's crashing, but I don't know how to change it for the Generic HostBuilder.

Here is my code that was working in .Net Core 2.2 :

namespace CompX.FunctionalTest.Web.Factory
{
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureServices(services =>
            {
                // Create a new service provider.
                var serviceProvider = new ServiceCollection()
                    .AddEntityFrameworkInMemoryDatabase()
                    .BuildServiceProvider();

                // Add a database context (ApplicationDbContext) using an in-memory 
                // database for testing.
                services.AddDbContext<ApplicationDbContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                    options.UseInternalServiceProvider(serviceProvider);
                });

                services.AddDbContext<AppIdentityDbContext>(options =>
                {
                    options.UseInMemoryDatabase("Identity");
                    options.UseInternalServiceProvider(serviceProvider);
                });

                services.AddIdentity<ApplicationUser, IdentityRole>()
                        .AddEntityFrameworkStores<AppIdentityDbContext>()
                        .AddDefaultTokenProviders();

                // Build the service provider.
                var sp = services.BuildServiceProvider();

                // Create a scope to obtain a reference to the database
                // context (ApplicationDbContext).
                using (var scope = sp.CreateScope())
                {
                    var scopedServices = scope.ServiceProvider;
                    var db = scopedServices.GetRequiredService<ApplicationDbContext>();
                    var loggerFactory = scopedServices.GetRequiredService<ILoggerFactory>();

                    var logger = scopedServices.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                    // Ensure the database is created.
                    db.Database.EnsureCreated();

                    try
                    {
                        // Seed the database with test data.
                        var userManager = scopedServices.GetRequiredService<UserManager<ApplicationUser>>();
                        AppIdentityDbContextSeed.SeedAsync(userManager).GetAwaiter().GetResult();
                    }
                    catch (Exception ex)
                    {
                        logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
                    }
                }
            });
        }
    }
}

I tried with TestServers from Microsoft.AspNetCore.TestHost, but it needs new WebHostBuilder() as parameters.

I also tried to pass this as parameters, but it didn't work as well :

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });

It can't find the .ConfigureWebHostDefaults()function.

Did anyone successfuly implemented a Test server in .Net Core 3.0 ? Thanks a lot !

PS : I'm kinda new to .Net Core

EDIT :

That's the error that I get from all the methods that try to make a new server : enter image description here

Here is the program.cs

namespace CompX.Web
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Here is the issue that I've created on github : https://github.com/aspnet/AspNetCore/issues/7754

like image 710
Fearware Avatar asked Feb 20 '19 18:02

Fearware


1 Answers

I finally got how to do it in 3.0. Here's the full walkthrough on how to do it for anyone that needs the solution :

  1. You need to have atleast .Net Core 3.0.0-preview2, since they added the WebApplicationFactory with IHostbuilder in this preview (https://github.com/aspnet/AspNetCore/pull/6585). You can find it here : https://dotnet.microsoft.com/download/dotnet-core/3.0

  2. Upgrade atleast theses packages to version 3.0.0 (https://github.com/aspnet/AspNetCore/issues/3756 and https://github.com/aspnet/AspNetCore/issues/3755):

    • Microsoft.AspNetCore.App Version=3.0.0-preview-19075-0444
    • Microsoft.AspNetCore.Mvc.Testing Version= 3.0.0-preview-19075-0444
    • Microsoft.Extensions.Hosting Version=3.0.0-preview.19074.2
  3. Remove now deprecated packages that are now included in Microsoft.AspNetCore.App:

    • Microsoft.AspNetCore Version=2.2.0
    • Microsoft.AspNetCore.CookiePolicy Version=2.2.0
    • Microsoft.AspNetCore.HttpsPolicy Version=2.2.0
    • Microsoft.AspNetCore.Identity Version=2.2.0
  4. If your were using services.AddIdentity in your WebApplicationFactory<Startup> builder, you'll need to delete it. Otherwise, you'll have a new error saying that you have already use that scheme for Identity.Application. It looks like the new WebApplicationFactory is using the one from Startup.cs now.

I had nothing else to modify. Hope it will be helpful for some people !

Update :

It was working well until I had to use another Integration C# file (e.g LoginTest.cs and ManageTest.cs). The problem was that when I ran my test, it would infinitly loop until I pressed CTRL + C. After that, it would display an Access Denied error.

Once again, I had to remove something from my WebApplicationFactory, the seeds :

            try
            {
                // Seed the database with test data.
                var userManager = scopedServices.GetRequiredService<UserManager<ApplicationUser>>();
                AppIdentityDbContextSeed.SeedAsync(userManager).GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
            }

It looks like the new WebApplicationFactory was trying to recreate the usermanager for each factories. I replaced my seeded user accounts with Guid.NewGuid().

Took me a while to figure it out. Hope it might help someone, once again.

like image 169
Fearware Avatar answered Sep 21 '22 14:09

Fearware