-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Decouple WebApplicationFactory from TestServer implementation #60370
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
Comments
- protected virtual TestServer CreateServer(IWebHostBuilder builder)
+ protected virtual ITestServer CreateServer(IWebHostBuilder builder) This is a breaking change. Someone could have a call like the following in their code.
https://learn.microsoft.com/en-us/dotnet/core/compatibility/library-change-rules |
It is indeed a breaking change, @halter73. This is intentional. And there will be an announcement for this change. I can, technically, utilize a pattern similar to the one I've used with the Server / TestServer properties, where I obsolete one and add a new method instead. What do you think? |
If possible, I would prefer a non-breaking version of this change, even if it's a bit less ergonomic. We can including the breaking change as an alternative design or vice-versa. |
With the properties it was easier, as they were read-only. Whereas with the CreateServer method, the user has to provide an implementation, and it is being called from within the WebApplicationFactory. So if we choose to take this route, the approach would be something like this: var server = CreateTestServer(); // existing method that we will obsolete
if (server is null)
server = CreateServer(); // the new method to This way we would prefer the new method over the old one, and eventually in the future will remove the CreateServer with the Thoughts? |
What if we had something like this? public virtual ITestServer CreateServerInterface(IWebHostBuilder builder) => CreateServer(builder); var server = CreateServerInterface(); I don't love ending the method name in "Interface", but something like that seems to strike a balance between making the new functionality easy to use without breaking anyone. I'm not sure we ever would need to remove |
Ok, I went with the following approach, which is consistent with the Server/TestServer properties I had: [Obsolete("This method is obsolete. Consider utilizing the CreateTestServer method instead.")]
protected virtual TestServer CreateServer(IWebHostBuilder builder) => new TestServer(builder);
protected virtual ITestServer CreateTestServer(IWebHostBuilder builder) => CreateServer(builder); |
@martincostello, @shanselman I know that you've done some work related to WebApplicationFactory in the past. I would appreciate your feedback regarding the approach I've taken here. As you can see in the |
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:
|
This looks reasonable to me. I'll be sure to try it out with my sample project (which is basically where the second motivating example comes from) once there's a version in a shipped preview to give further feedback on. The obsolete way suggested is definitely better than the hard-breaking option, as I've written some internal libraries that derive from WAF to enapsulate some of these workarounds and it avoids breaking them at runtime if used in a .NET 10 application without recompiling for Thinking out loud, is there any value in changing - public void Initialize()
+ public virtual void Initialize() Would the proposal here also tackle #33846, or is that a separate ask? |
Thank you, @martincostello. |
I like it |
Closing this in favor of an alternative approach: #60758 |
Uh oh!
There was an error while loading. Please reload this page.
Background and Motivation
We have a long-standing popular issue tracking improvement of automated browser testing with real server. Part of the ask is to decouple the WebApplicationFactory from the TestServer implementation, as they're currently [tightly coupled] (
aspnetcore/src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs
Line 29 in 2edcf3b
Unfortunately, the TestServer is also exposed in WebApplicationFactory via a public property Server. Hence, decoupling requires an API change proposed below.
The second change is 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:Another example of not-so-great solution that the community came up with can be found here:
Here, the developer calls the
CreateDefaultClient()
in a derived class to force initialization.With the proposed changes, customers can now simply call
server.Initialize()
instead which is more intuitive.Proposed API
New abstraction / interface to depend on instead of the TestServer implementation.
Usage / API change in WebApplicationFactory:
Usage Examples
With this change, developers will be able to avoid creating artificial server instances only to fulfil the contract. Instead, here is an example of how a developer will override the newly added
CreateTestServer
method, that will utilize the very same server created by the host and use an adapter to utilize it as ITestServer:Note, that the
KestrelTestServerAdapter
type is something the developers will need to come up with. Here is a sample implementation:Sample IServer adapter
Below is the adapter class to encapsulate real IServer implementation and expose it as ITestServer.
Alternative Designs
Risks
The text was updated successfully, but these errors were encountered: