Skip to content

Add UseKestrel() API to the WebApplicationFactory #60758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mkArtakMSFT opened this issue Mar 5, 2025 · 7 comments · Fixed by #60635
Closed

Add UseKestrel() API to the WebApplicationFactory #60758

mkArtakMSFT opened this issue Mar 5, 2025 · 7 comments · Fixed by #60635
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-mvc-testing MVC testing package
Milestone

Comments

@mkArtakMSFT
Copy link
Contributor

mkArtakMSFT commented Mar 5, 2025

Background and Motivation

We have a long-standing popular issue tracking improvement of automated browser testing with real server.

To simplify this process, customers were asking for decoupling the WebApplicationFactory from TestServer, as they're currently tightly coupled. That would allow one to implement alternative implementation and use that in WebApplicaitonFactory.

While that was an option to pursue an alternative (perhaps a more straight forward) approach has been proposed during a design discussion with @captainsafia , @javiercn and @halter73. That is, instead of focusing on achieving the goal utilizing the current constructs, come up with a more straight forward way to enable utilizing Kestrel server in WebApplicationFactory. This proposal was great, because that's what many customers wanted to accomplish anyway and that's why they wanted to have some way to not utilize the TestServer implementation.

Another issue is that today exposing the initialization logic publicly. Without this change, customers had to call the CreateClient() API which would internally initialize the server but wouldn't use the returned HttpClient instance. This will make it more intuitive and avoid creating unnecessary objects. Here is a screenshot from a blogpost pointing out this odd usage pattern:

Image

Another example of not-so-great solution that the community came up with can be found here:

Image

Here, the developer calls the CreateDefaultClient() in a derived class to force initialization.

With the proposed changes, customers won't need to worry about this, as the real server initialization is still going to be handled by WebApplicationFactory, so there will be no need to interfere in this process. However, there may still be a need to initialize the server without explicitly getting a client. This is useful in browser-based testing scenarios with Selenium, where the framework will be interacting with the browser and the browser will handle the communication with the server internally.

Proposed API

PR: #60635

namespace Microsoft.AspNetCore.Mvc.Testing;

public class WebApplicationFactory<TEntryPoint>
{
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.StartServer() -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel() -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel(int port) -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel(System.Action<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions!>! configureKestrelOptions) -> void

Usage Examples

UseKestrel API(s)

Most of the time, developers use some helper class that encapsulates all the configuration of the WebApplicationFactory. To configure that type to utilize Kestrel server in tests, developers can call the UseKestrel() API from the constructor, as shown below.

public class KestrelBasedWapFactory : WebApplicationFactory<SimpleWebSite.Startup>
{
    public KestrelBasedWapFactory()
    {
        this.UseKestrel();
    }
}

Alternatively, consumers could call the UseKestrel() on the WebApplicationFactory instance directly, if they're not utilizing and intermediate helper. That way, the UseKestrel will need to be called during the Test initialization / setup phase.

The UseKestrel overloads can be called one after another. For example, the developer may want to specify both a custom port for the Kestrel to use as well as customize the server when it starts. To achieve that, two different overloads should be called:

int dynamicPortNumber = 0;
this.UseKestrel(dynamicPortNumber);
this.UseKestrel(options=>{ /* Customize the options for kestrel here */ });

Note: the UseKestrel() call is not reversible and must be called before the WebApplicationFactory is initialized, which is done through the StartServer() call.

StartServer API

This API allows starting the underlying server of the WebApplicationFactory without the need of creating a client. This comes handy is scenarios related to browser-based testing, where developers will be interacting with the server only through the browser instance provided via test-framework. Below is an excerpt from a test which utilizes Selenium:

public BrowserBasedTests(WebApplicationFactory<MyApp.Program> factory){
    _factory = factory;
    _factory.StartServer();
}

[Fact]
public void BrowserBasedTest(){
 Browser.Navigate().GoToUrl("[url that the server listens to]");

var selector = By.TagName("h1");
Assert.Equal("Page title is here", Browser.FindElement(selector).Text);
}

Alternative Designs

Here is the alternative that was considered first: #60247

Risks

Right now, there are no mechanisms in place to enable much customization of the kestrel creation process, other than interacting with the DI before the server creation. This may become something that customers need, but I hold off from exposing the CreateKestrelServer to the customers to gauge the need for it first. We have time so if needed, that can be done later. Mitigated by the UseKestrel (Action) API

@mkArtakMSFT mkArtakMSFT added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Mar 5, 2025
@ghost ghost added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Mar 5, 2025
@mkArtakMSFT mkArtakMSFT added api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews feature-mvc-testing MVC testing package labels Mar 5, 2025
@mkArtakMSFT mkArtakMSFT linked a pull request Mar 5, 2025 that will close this issue
@halter73
Copy link
Member

halter73 commented Mar 6, 2025

API Review Notes:

  • Should Initialize() be made async?
    • No
  • Should Initialize() be renamed to InitializeServer()?
    • Or Start()? Or Run()? StartServer()?
    • StartServer() it is.
  • Should UseKestrel() take a port argument? e.g. UseKestrel(port: 5005)
    • This could be done with the IServerAddressesFeature.
    • We could add an overload later.
  • Can we keep the Server property nullable and then throw if it's accessed after calling UseKestrel()?
    • Yes.

API Approved!

namespace Microsoft.AspNetCore.Mvc.Testing;

public class WebApplicationFactory<TEntryPoint>
{
+      public void StartServer();
+      public void UseKestrel();
}

@halter73 halter73 added api-approved API was approved in API review, it can be implemented and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Mar 6, 2025
@Tratcher
Copy link
Member

Tratcher commented Mar 6, 2025

Can you show an example of configuring Kestrel?

How about WebListener?

@halter73
Copy link
Member

halter73 commented Mar 7, 2025

@Tratcher You can see the PR here: #60635

It's currently Kestrel-only.

@mkArtakMSFT
Copy link
Contributor Author

I'm going to revoke the approval for this one, as I plan to follow @halter73's and @Tratcher's suggestion to make the Kestrel server more configurable when using the UseKestrel API. Will update the proposal with the details soon.

@mkArtakMSFT mkArtakMSFT added api-suggestion Early API idea and discussion, it is NOT ready for implementation and removed api-approved API was approved in API review, it can be implemented labels Mar 7, 2025
@mkArtakMSFT
Copy link
Contributor Author

mkArtakMSFT commented Mar 7, 2025

Updated Proposal

(Also updated the description)

Below are the APIs to be used:

+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.StartServer() -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel() -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel(int port) -> void
+ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.UseKestrel(System.Action<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions!>! configureKestrelOptions) -> void

@mkArtakMSFT mkArtakMSFT added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Mar 17, 2025
Copy link
Contributor

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@mkArtakMSFT mkArtakMSFT self-assigned this Mar 19, 2025
@halter73
Copy link
Member

API Review Notes:

  • We had already approved the UseKestrel() and StartServer() methods, and this just adds two UseKestrel() overloads:
    • UseKestrel(int port)
    • UseKestrel(Action<KestrelServerOptions> configureKestrelOptions)
  • The overload signature and parameter names seem reasonable
  • We could see a scenario where someone wanted to configure both the port and provide an options callback but think that’s niche.
    • Things should still work if I just called both methods
    • waf.UseKestrel(0);
      waf.UseKestrel(options =>
      {
         options.AllowSynchronousIO = true;
      });
  • Should UseKestrel return this (i.e. WebApplicationFactory<TEntryPoint>) to be more consistent with other Use... methods?
    • No. WAF is not really a builder type, and it's unclear what you'd chain off it.

Updated API Approved!

namespace Microsoft.AspNetCore.Mvc.Testing;

public class WebApplicationFactory<TEntryPoint>
{
+    public void StartServer();
+    public void UseKestrel();
+    public void UseKestrel(int port);
+    public void UseKestrel(Action<KestrelServerOptions> configureKestrelOptions);
}

@halter73 halter73 added api-approved API was approved in API review, it can be implemented and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Mar 24, 2025
mkArtakMSFT added a commit that referenced this issue Mar 28, 2025
- Added `UseKestrel(...)` APIs to the WebApplicationFactory<T> type. The API configures the WAF, so that later during initialization it will use Kestrel, instead of a TestServer for WebHostBuilder-based applications.
- Two different overloads (described in the API Proposal #60758) are added
- Renamed the `EnsureServer()` private method to `StartServer()` and made it public, so that consumers can call it directly, without the need of creating a client, as in many situations customers didn't need a client to interact with.


This is an alternative design to enabling real server usage with WebApplicationFactory, which was considered here: #60247

Fixes #4892
@mkArtakMSFT mkArtakMSFT added this to the 10.0-preview4 milestone Mar 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates feature-mvc-testing MVC testing package
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants