Skip to content

Commit 8683598

Browse files
committed
- Created an ITestServer abstraction to replace the TestServer dependency with from in the WebApplicationFactory.
- Updated the WebApplicationFactory to depend on the ITestServer abstraction
1 parent 9f3e60c commit 8683598

11 files changed

+84
-19
lines changed
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Net.Http;
5+
using Microsoft.AspNetCore.Hosting;
6+
using Microsoft.AspNetCore.Hosting.Server;
7+
8+
namespace Microsoft.AspNetCore.TestHost;
9+
10+
/// <summary>
11+
/// A contract for a test server implementation.
12+
/// </summary>
13+
public interface ITestServer : IServer
14+
{
15+
/// <summary>
16+
/// Gets the web host associated with the test server.
17+
/// </summary>
18+
IWebHost Host { get; }
19+
20+
/// <summary>
21+
/// Creates a new <see cref="HttpMessageHandler"/> for processing HTTP requests against the test server.
22+
/// </summary>
23+
/// <returns>A new <see cref="HttpMessageHandler"/> instance.</returns>
24+
HttpMessageHandler CreateHandler();
25+
26+
/// <summary>
27+
/// Creates a new <see cref="HttpClient"/> for processing HTTP requests against the test server.
28+
/// </summary>
29+
/// <returns></returns>
30+
HttpClient CreateClient();
31+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
#nullable enable
2+
Microsoft.AspNetCore.TestHost.ITestServer
3+
Microsoft.AspNetCore.TestHost.ITestServer.CreateClient() -> System.Net.Http.HttpClient!
4+
Microsoft.AspNetCore.TestHost.ITestServer.CreateHandler() -> System.Net.Http.HttpMessageHandler!
5+
Microsoft.AspNetCore.TestHost.ITestServer.Host.get -> Microsoft.AspNetCore.Hosting.IWebHost!

src/Hosting/TestHost/src/TestServer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.TestHost;
1414
/// <summary>
1515
/// An <see cref="IServer"/> implementation for executing tests.
1616
/// </summary>
17-
public class TestServer : IServer
17+
public class TestServer : ITestServer
1818
{
1919
private readonly IWebHost? _hostInstance;
2020
private bool _disposed;

src/Mvc/Mvc.Testing/src/PublicAPI.Shipped.txt

-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaul
1616
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateDefaultClient(System.Uri! baseAddress, params System.Net.Http.DelegatingHandler![]! handlers) -> System.Net.Http.HttpClient!
1717
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Dispose() -> void
1818
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Factories.get -> System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!>!
19-
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer!
2019
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WebApplicationFactory() -> void
2120
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.WithWebHostBuilder(System.Action<Microsoft.AspNetCore.Hosting.IWebHostBuilder!>! configuration) -> Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint!>!
2221
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions
@@ -41,7 +40,6 @@ virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Conf
4140
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> void
4241
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHost(Microsoft.Extensions.Hosting.IHostBuilder! builder) -> Microsoft.Extensions.Hosting.IHost!
4342
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateHostBuilder() -> Microsoft.Extensions.Hosting.IHostBuilder?
44-
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.TestServer!
4543
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateWebHostBuilder() -> Microsoft.AspNetCore.Hosting.IWebHostBuilder?
4644
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Dispose(bool disposing) -> void
4745
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.DisposeAsync() -> System.Threading.Tasks.ValueTask
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Initialize() -> void
3+
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.Server.get -> Microsoft.AspNetCore.TestHost.TestServer?
4+
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.TestServer.get -> Microsoft.AspNetCore.TestHost.ITestServer?
5+
virtual Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory<TEntryPoint>.CreateServer(Microsoft.AspNetCore.Hosting.IWebHostBuilder! builder) -> Microsoft.AspNetCore.TestHost.ITestServer!

src/Mvc/Mvc.Testing/src/Resources.resx

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<root>
33
<!--
44
Microsoft ResX Schema
@@ -120,6 +120,9 @@
120120
<data name="InvalidAssemblyEntryPoint" xml:space="preserve">
121121
<value>The provided Type '{0}' does not belong to an assembly with an entry point. A common cause for this error is providing a Type from a class library.</value>
122122
</data>
123+
<data name="InvalidTestServerConfiguration" xml:space="preserve">
124+
<value>The host doesn't contain a valid ITestServer configuration.</value>
125+
</data>
123126
<data name="MissingBuilderMethod" xml:space="preserve">
124127
<value>No method 'public static {0} CreateHostBuilder(string[] args)' or 'public static {1} CreateWebHostBuilder(string[] args)' found on '{2}'. Alternatively, {3} can be extended and '{4}' or '{5}' can be overridden to provide your own instance.</value>
125128
</data>

src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs

+36-11
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ namespace Microsoft.AspNetCore.Mvc.Testing;
2222
/// </summary>
2323
/// <typeparam name="TEntryPoint">A type in the entry point assembly of the application.
2424
/// Typically the Startup or Program classes can be used.</typeparam>
25+
/// <remarks>The default behavior of the <see cref="CreateServer(IWebHostBuilder)"/> implementation
26+
/// creates a new <see cref="TestHost.TestServer"/> instance as an in-memory server to utilize for testing.
27+
/// If the developers wants, they can override the <see cref="CreateServer(IWebHostBuilder)"/> method to return a customer <see cref="ITestServer"/> implementation,
28+
/// and provide an adapter over a real web server, if needed. If done so, the <see cref="CreateClient()"/> and similar methods will
29+
/// create <see cref="HttpClient"/> instances configured to interact with the provided test server instead.</remarks>
2530
public partial class WebApplicationFactory<TEntryPoint> : IDisposable, IAsyncDisposable where TEntryPoint : class
2631
{
2732
private bool _disposed;
2833
private bool _disposedAsync;
29-
private TestServer? _server;
34+
private ITestServer? _server;
3035
private IHost? _host;
3136
private Action<IWebHostBuilder> _configuration;
3237
private readonly List<HttpClient> _clients = new();
@@ -71,11 +76,23 @@ public WebApplicationFactory()
7176
/// <summary>
7277
/// Gets the <see cref="TestServer"/> created by this <see cref="WebApplicationFactory{TEntryPoint}"/>.
7378
/// </summary>
74-
public TestServer Server
79+
[Obsolete("This property is obsolete. Consider utilizing the TestServer property instead.")]
80+
public TestServer? Server
7581
{
7682
get
7783
{
78-
EnsureServer();
84+
return TestServer as TestServer;
85+
}
86+
}
87+
88+
/// <summary>
89+
/// Gets the <see cref="ITestServer"/> instance created by the underyling <see cref="CreateServer(IWebHostBuilder)"/> call.
90+
/// </summary>
91+
public ITestServer? TestServer
92+
{
93+
get
94+
{
95+
Initialize();
7996
return _server;
8097
}
8198
}
@@ -87,7 +104,7 @@ public virtual IServiceProvider Services
87104
{
88105
get
89106
{
90-
EnsureServer();
107+
Initialize();
91108
return _host?.Services ?? _server.Host.Services;
92109
}
93110
}
@@ -136,8 +153,12 @@ internal virtual WebApplicationFactory<TEntryPoint> WithWebHostBuilderCore(Actio
136153
return factory;
137154
}
138155

156+
/// <summary>
157+
/// Initializes the instance by configurating the host builder.
158+
/// </summary>
159+
/// <exception cref="InvalidOperationException">Thrown if the provided <typeparamref name="TEntryPoint"/> type has no factory method.</exception>
139160
[MemberNotNull(nameof(_server))]
140-
private void EnsureServer()
161+
public void Initialize()
141162
{
142163
if (_server != null)
143164
{
@@ -211,7 +232,11 @@ private void ConfigureHostBuilder(IHostBuilder hostBuilder)
211232
webHostBuilder.UseTestServer();
212233
});
213234
_host = CreateHost(hostBuilder);
214-
_server = (TestServer)_host.Services.GetRequiredService<IServer>();
235+
_server = _host.Services.GetRequiredService<IServer>() as ITestServer;
236+
if (_server is null)
237+
{
238+
throw new InvalidOperationException(Resources.InvalidTestServerConfiguration);
239+
}
215240
}
216241

217242
private void SetContentRoot(IWebHostBuilder builder)
@@ -426,7 +451,7 @@ private static void EnsureDepsFile()
426451
/// <param name="builder">The <see cref="IWebHostBuilder"/> used to
427452
/// create the server.</param>
428453
/// <returns>The <see cref="TestServer"/> with the bootstrapped application.</returns>
429-
protected virtual TestServer CreateServer(IWebHostBuilder builder) => new(builder);
454+
protected virtual ITestServer CreateServer(IWebHostBuilder builder) => new TestServer(builder);
430455

431456
/// <summary>
432457
/// Creates the <see cref="IHost"/> with the bootstrapped application in <paramref name="builder"/>.
@@ -476,7 +501,7 @@ public HttpClient CreateClient(WebApplicationFactoryClientOptions options) =>
476501
/// <returns>The <see cref="HttpClient"/>.</returns>
477502
public HttpClient CreateDefaultClient(params DelegatingHandler[] handlers)
478503
{
479-
EnsureServer();
504+
Initialize();
480505

481506
HttpClient client;
482507
if (handlers == null || handlers.Length == 0)
@@ -606,7 +631,7 @@ public virtual async ValueTask DisposeAsync()
606631

607632
private sealed class DelegatedWebApplicationFactory : WebApplicationFactory<TEntryPoint>
608633
{
609-
private readonly Func<IWebHostBuilder, TestServer> _createServer;
634+
private readonly Func<IWebHostBuilder, ITestServer> _createServer;
610635
private readonly Func<IHostBuilder, IHost> _createHost;
611636
private readonly Func<IWebHostBuilder?> _createWebHostBuilder;
612637
private readonly Func<IHostBuilder?> _createHostBuilder;
@@ -615,7 +640,7 @@ private sealed class DelegatedWebApplicationFactory : WebApplicationFactory<TEnt
615640

616641
public DelegatedWebApplicationFactory(
617642
WebApplicationFactoryClientOptions options,
618-
Func<IWebHostBuilder, TestServer> createServer,
643+
Func<IWebHostBuilder, ITestServer> createServer,
619644
Func<IHostBuilder, IHost> createHost,
620645
Func<IWebHostBuilder?> createWebHostBuilder,
621646
Func<IHostBuilder?> createHostBuilder,
@@ -633,7 +658,7 @@ public DelegatedWebApplicationFactory(
633658
_configuration = configureWebHost;
634659
}
635660

636-
protected override TestServer CreateServer(IWebHostBuilder builder) => _createServer(builder);
661+
protected override ITestServer CreateServer(IWebHostBuilder builder) => _createServer(builder);
637662

638663
protected override IHost CreateHost(IHostBuilder builder) => _createHost(builder);
639664

src/Mvc/test/Mvc.FunctionalTests/ApiExplorerTest.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1573,7 +1573,7 @@ public async Task ApiExplorer_LogsInvokedDescriptionProvidersOnStartup()
15731573
[Fact]
15741574
public void ApiExplorer_BuildsMetadataForActionWithTypedResult()
15751575
{
1576-
var apiDescCollectionProvider = Factory.Server.Services.GetService<IApiDescriptionGroupCollectionProvider>();
1576+
var apiDescCollectionProvider = Factory.Services.GetService<IApiDescriptionGroupCollectionProvider>();
15771577
var testGroupName = nameof(ApiExplorerWithTypedResultController).Replace("Controller", string.Empty);
15781578
var group = apiDescCollectionProvider.ApiDescriptionGroups.Items.Where(i => i.GroupName == testGroupName).SingleOrDefault();
15791579
Assert.NotNull(group);

src/Mvc/test/Mvc.FunctionalTests/ComponentRenderingFunctionalTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ private HttpClient CreateClient(
198198

199199
// We configure the inner handler with a handler to this TestServer instance so that calls to the
200200
// server can get routed properly.
201-
loopHandler.InnerHandler = fixture.Server.CreateHandler();
201+
loopHandler.InnerHandler = fixture.TestServer.CreateHandler();
202202

203203
void ConfigureTestWeatherForecastService(IServiceCollection services) =>
204204
// We configure the test service here with an HttpClient that uses this loopback handler to talk

src/Mvc/test/Mvc.FunctionalTests/Infrastructure/MvcTestFixture.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
4242
});
4343
}
4444

45-
protected override TestServer CreateServer(IWebHostBuilder builder)
45+
protected override ITestServer CreateServer(IWebHostBuilder builder)
4646
{
4747
var originalCulture = CultureInfo.CurrentCulture;
4848
var originalUICulture = CultureInfo.CurrentUICulture;

src/Mvc/test/Mvc.FunctionalTests/TestingInfrastructureInheritanceTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
141141
base.ConfigureWebHost(builder);
142142
}
143143

144-
protected override TestServer CreateServer(IWebHostBuilder builder)
144+
protected override ITestServer CreateServer(IWebHostBuilder builder)
145145
{
146146
CreateServerCalled = true;
147147
return base.CreateServer(builder);

0 commit comments

Comments
 (0)