'Using WebApplicationFactory to do E2E testing with .Net 6 (minimal api)
Im trying to get E2E/UI testing (selenium, playwright) to work with my unit testing framework.
The basic idea it to use MSTest and the WebApplicationFactory to spin up a "real server" from within my unit tests. The reason for doing this would be to simply avoid having to deploy/release my application for testing (I guess this could be done by using containers etc, sadly.. Im not allowed to use containers). Im also thinking that doing it this wasy would be a "neat" way of mocking any code that calls external services, and be able to create multiple tests with different scenarios for those external calls.
I have searched the web for a way of doing this, but all I can find are posts about how to do this in previous .Net versions (2.1-5), but since .Net 6 the "startup ceremonial" code has changed and the standard way is now to use the minimal API.
Here is a blog post from Scott.H where he is basically doing exactly what Im planing to do, but with .Net 2.1: https://www.hanselman.com/blog/real-browser-integration-testing-with-selenium-standalone-chrome-and-aspnet-core-21
What I have done so far is creating a custom class that inherits from WebApplicationFactory.
basically:
class MyAppFactory : WebApplicationFactory<Program> {
}
And I can use that perfectly fine for integration testing. However.. the server thats initialized when using that class does not accept http-calls, so I cant reach that using a web browser, and neither can selenium.
I tried to follow Scotts blog post. But for some reason the:
protected override TestServer CreateServer(IWebHostBuilder builder)
Is never called.. (not sure if that has with minimal APIs and .Net 6 to do).
Has anyone managed to use the WebApplicationFactory and .Net 6 Minimal API to spin up an "actual server" in memory that accepts http-calls?
Solution 1:[1]
It looks like you need to call CreateServer explicitly. This blog post shows how the author fixed this when migrating from 2.2 to 3.1 I don't know if there are better solutions in .NET 6, but this should at least fix your issue.
Solution 2:[2]
I faced the same problem while migrating to .NET 6 and found the solution thanks to this blog post from Marius Steinbach.
Steps:
- Make sure there is the Program definition in
Program.cs
file
var builder = WebApplication.CreateBuilder(args);
// adds services to the container
builder.Services.AddRazorPages();
var app = builder.Build();
// configures the HTTP request pipeline
app.UseExceptionHandler("/Error");
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
public partial class Program { }
- Create a new class
WebApplicationFactoryFixture
(sample)
public class WebApplicationFactoryFixture<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public string HostUrl { get; set; } = "https://localhost:5001"; // we can use any free port
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseUrls(HostUrl);
}
protected override IHost CreateHost(IHostBuilder builder)
{
var dummyHost = builder.Build();
builder.ConfigureWebHost(webHostBuilder => webHostBuilder.UseKestrel());
var host = builder.Build();
host.Start();
return dummyHost;
}
}
- Use this factory in the test class (sample)
public class SmokeSeleniumTest : IClassFixture<WebApplicationFactoryFixture<Program>>
{
private readonly string _webUrl = "https://localhost:7112";
public SmokeSeleniumTest(WebApplicationFactoryFixture<Program> factory)
{
factory.HostUrl = _webUrl;
factory.CreateDefaultClient();
var chromeOptions = new ChromeOptions();
// ...
WebDriver = new ChromeDriver(chromeDriverLocation, chromeOptions);
}
protected IWebDriver WebDriver { get; }
[Theory]
[InlineData("/", "Welcome")]
[InlineData("/Index", "Welcome")]
[InlineData("/Privacy", "Privacy Policy")]
[InlineData("/Error", "Error.")]
public void Get_EndpointsReturnSuccessAndCorrectContentType(string url, string expected)
{
// Arrange & Act
WebDriver.Navigate().GoToUrl($"{_webUrl}{url}");
// Assert
WebDriver.FindElement(By.TagName("h1"), 30);
WebDriver.FindElement(By.TagName("h1")).Text.Should().Contain(expected);
}
}
It works!
Samples are validated through a CI pipeline in Azure DevOps in this repository.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | CameO73 |
Solution 2 | devpro |