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,
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);
}
}
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>();
}
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.
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.
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.
This method worked for me
var client = _factory
.WithWebHostBuilder(builder => builder.UseSolutionRelativeContentRoot("relative/path/of/project/under/test"))
.CreateClient();
See How the test infrastructure infers the app content root path for more information.