WebApplicationFactory throws error that contentRootPath does not exist in ASP.NET Core integration test
Asked Answered
U

6

29

I have a ASP.NET Core project with some simple Razor pages and a Web API controller.

I'm using Clean Architecture as a starting point. I've renamed the project names, removed the MVC stuff and added some of my own code. Everything runs and works.

However, the integration tests throw the following error when calling factory.CreateClient():

Test Name:  ToDo.Tests.Integration.Web.HomeControllerIndexShould.ReturnViewWithCorrectMessage
Test FullName:  ToDo.Tests.Integration.Web.HomeControllerIndexShould.ReturnViewWithCorrectMessage
Test Source:    C:\Source\Utopia\tests\ToDo.Tests\Integration\Web\HomeControllerIndexShould.cs : line 18
Test Outcome:   Failed
Test Duration:  0:00:00,001

Result StackTrace:  
at Microsoft.AspNetCore.Hosting.Internal.HostingEnvironmentExtensions.Initialize(IHostingEnvironment hostingEnvironment, String contentRootPath, WebHostOptions options)
   at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
   at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
   at Microsoft.AspNetCore.TestHost.TestServer..ctor(IWebHostBuilder builder, IFeatureCollection featureCollection)
   at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateServer(IWebHostBuilder builder)
   at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.EnsureServer()
   at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
   at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
   at ToDo.Tests.Integration.Web.HomeControllerIndexShould..ctor(CustomWebApplicationFactory`1 factory) in C:\Source\Utopia\tests\ToDo.Tests\Integration\Web\HomeControllerIndexShould.cs:line 14
Result Message: 
System.ArgumentException : The content root 'C:\Source\Utopia\ToDo.Web' does not exist.
Parameter name: contentRootPath

I've tried configuring the custom WebApplicationFactory by using builder.UseContentRoot and builder.UseSolutionRelativeContentRoot but it keeps throwing the same error no matter what values I use for the ContentRoot methods.

I don't know why my tests are failing while the one in the Clean Architecture sample are working. I also don't know how to fix it.

Any pointers are highly appreciated!

Unruly answered 12/11, 2018 at 20:44 Comment(4)
Somehow I only just ran into this myself with the project. I added the accepted answer's solution to the 3.1 update of CleanArchitecture and it seems to have fixed it.Lim
@Lim I do wonder what triggered the issue... somehow something changed between your 2.2 Clean Architecture and 3.1/my code.Unruly
If you run into problems in the future please file an issue on the repo, too! Thanks!Lim
This happened to me when I tried to copy a test project from one solution to another. The namespaces across my test project name, it's containing directory, code files, everywhere had to align perfectly.Vocalic
K
25

This method worked for me

var client = _factory
    .WithWebHostBuilder(builder => builder.UseSolutionRelativeContentRoot("relative/path/of/project/under/test"))
    .CreateClient();

How the test infrastructure infers the app content root path says (with my markup added)

The WebApplicationFactory constructor infers the app content root path by searching for a WebApplicationFactoryContentRootAttribute on the assembly containing the integration tests with a key equal to the TEntryPoint assembly System.Reflection.Assembly.FullName. In case an attribute with the correct key isn't found, WebApplicationFactory falls back to searching for a solution file (.sln) and appends the TEntryPoint assembly name to the solution directory. The app root directory (the content root path) is used to discover views and content files.

Kisner answered 20/12, 2018 at 19:17 Comment(1)
Thanks a lot, but please add sample for WebApplicationFactoryContentRootAttributeKoah
T
11

My solution for this problem is define WebApplicationFactory with Application Startup but setup WebHostBuilder with TestStartup.

Example:

public class MyApplicationFactory : WebApplicationFactory<Startup>
{
    protected override IWebHostBuilder CreateWebHostBuilder()
    {
        return WebHost.CreateDefaultBuilder();
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseStartup<TestStartup>();

        base.ConfigureWebHost(builder);
    }
}
Telecast answered 13/8, 2019 at 10:49 Comment(0)
N
8

It seems that WebApplicationFactory should use the real Startup class as the type of argument:

class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
     protected override IWebHostBuilder CreateWebHostBuilder()
     {
         return WebHost.CreateDefaultBuilder<TestableStartup>();
     }
}

Note that Startup is the type on true SUT code and TestableStartup is the TestingStartup configuration.

Neuropsychiatry answered 3/6, 2019 at 5:26 Comment(0)
M
7

If you are using ReSharper it may well be related to the version. We found the issue with version 2018.3. Adding UseSolutionRelativeContentRoot resolved it.

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
            builder.UseKestrel()
            .UseSolutionRelativeContentRoot("")
            .ConfigureAppConfiguration((context, configBuilder) =>
            {
              // Config code
            })
            .UseStartup<Startup>();
    }
Marks answered 14/1, 2019 at 10:11 Comment(1)
That was the fix for me as the projects were not in the rootMariahmariam
G
5

I'm not entirely sure what you're talking about, but you shouldn't be configuring stuff like this in a test project, in the first place. Instead, you should create a class like TestStartup and inherit from the SUT's Startup class. In the SUT's Startup class, you should factor out things like your DB setup and such into virtual private methods, which you can then override on TestStartup. For example, you could create a method like:

private virtual void ConfigureDatabase(IServiceCollection services)
{
    services.AddDbContext<MyContext>(o =>
        o.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

Then in your TestStartup, you'd add something like:

private override void ConfigureDatabase(IServiceCollection services)
{
    var databaseName = Guid.NewGuid().ToString();
    services.AddDbContext<MyContext>(o =>
        o.UseInMemoryDatabase(databaseName));
}

Then, when setting up your factory for testing, you tell it to use your TestStartup:

var client = factory.WithWebHostBuilder(b => b.UseStartup<TestStartup>()).CreateClient();

Or, you can create you own custom WebApplicationFactory and set it there:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<RazorPagesProject.Startup>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseStartup<TestStartup>();
    }
}

Just bear in mind that the TStartup generic type param is for getting the entry point assembly, so you'd still put Startup there.

The main point of this is that you don't need to repeat all your startup configuration, and then remember to keep in sync. Your test client will use the exact same startup config your actual apps uses, except for a few keep replacements like using an in-memory database.

Grammer answered 13/11, 2018 at 14:44 Comment(1)
Thanks for the suggestion but after trying it I'm still getting the same error that contentRootPath isn't set.Unruly
K
4

I have a really simple project structure and ran into the same issue. Project structure:

-repo_root
 - myProject
   - myProject.csproj
   - src
 - myProjectTest
   - myProjectTest.csproj
   - tests

All of the above answers didn't work for me. I went throught the docs and other sources related to the WebApplicationFactoryContentRootAttribute to no avail. I also looked into the WebApplicationFactory implementation and tried to use the TEST_CONTENTROOT_APPNAME in settings to no avail.

What did help in the end was creating an empty myProject.sln file in the repo_root.

Kaela answered 24/9, 2020 at 11:51 Comment(1)
Adding an .sln file was the only thing that helped in my case, I wasn't able to make WebApplicationFactoryContentRootAttribute work, thanks!Merlinmerlina

© 2022 - 2024 — McMap. All rights reserved.