Add TestServer support for generic WebHost #1583

This commit is contained in:
Chris Ross (ASP.NET) 2018-11-16 11:01:01 -08:00
parent f6cda4fab7
commit 241d2c13df
6 changed files with 160 additions and 14 deletions

View File

@ -0,0 +1,35 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.AspNetCore.TestHost
{
public static class HostBuilderTestServerExtensions
{
/// <summary>
/// Retrieves the TestServer from the host services.
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public static TestServer GetTestServer(this IHost host)
{
return (TestServer)host.Services.GetRequiredService<IServer>();
}
/// <summary>
/// Retrieves the test client from the TestServer in the host services.
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public static HttpClient GetTestClient(this IHost host)
{
return host.GetTestServer().CreateClient();
}
}
}

View File

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
@ -15,28 +16,48 @@ namespace Microsoft.AspNetCore.TestHost
{
public class TestServer : IServer
{
private const string ServerName = nameof(TestServer);
private IWebHost _hostInstance;
private bool _disposed = false;
private IHttpApplication<Context> _application;
/// <summary>
/// For use with IHostBuilder or IWebHostBuilder.
/// </summary>
public TestServer()
: this(new FeatureCollection())
{
}
/// <summary>
/// For use with IHostBuilder or IWebHostBuilder.
/// </summary>
/// <param name="featureCollection"></param>
public TestServer(IFeatureCollection featureCollection)
{
Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));
}
/// <summary>
/// For use with IWebHostBuilder.
/// </summary>
/// <param name="builder"></param>
public TestServer(IWebHostBuilder builder)
: this(builder, new FeatureCollection())
{
}
/// <summary>
/// For use with IWebHostBuilder.
/// </summary>
/// <param name="builder"></param>
/// <param name="featureCollection"></param>
public TestServer(IWebHostBuilder builder, IFeatureCollection featureCollection)
: this(featureCollection)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (featureCollection == null)
{
throw new ArgumentNullException(nameof(featureCollection));
}
Features = featureCollection;
var host = builder.UseServer(this).Build();
host.StartAsync().GetAwaiter().GetResult();
@ -49,16 +70,22 @@ namespace Microsoft.AspNetCore.TestHost
{
get
{
return _hostInstance;
return _hostInstance
?? throw new InvalidOperationException("The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.");
}
}
public IFeatureCollection Features { get; }
private IHttpApplication<Context> Application
{
get => _application ?? throw new InvalidOperationException("The server has not been started or no web application was configured.");
}
public HttpMessageHandler CreateHandler()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
return new ClientHandler(pathBase, _application);
return new ClientHandler(pathBase, Application);
}
public HttpClient CreateClient()
@ -69,7 +96,7 @@ namespace Microsoft.AspNetCore.TestHost
public WebSocketClient CreateWebSocketClient()
{
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
return new WebSocketClient(pathBase, _application);
return new WebSocketClient(pathBase, Application);
}
/// <summary>
@ -93,7 +120,7 @@ namespace Microsoft.AspNetCore.TestHost
throw new ArgumentNullException(nameof(configureContext));
}
var builder = new HttpContextBuilder(_application);
var builder = new HttpContextBuilder(Application);
builder.Configure(context =>
{
var request = context.Request;

View File

@ -6,12 +6,21 @@ using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.TestHost
{
public static class WebHostBuilderExtensions
{
public static IWebHostBuilder UseTestServer(this IWebHostBuilder builder)
{
return builder.ConfigureServices(services =>
{
services.AddSingleton<IServer, TestServer>();
});
}
public static IWebHostBuilder ConfigureTestServices(this IWebHostBuilder webHostBuilder, Action<IServiceCollection> servicesConfiguration)
{
if (webHostBuilder == null)

View File

@ -2,20 +2,32 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Hosting
{
public static class HostingAbstractionsHostBuilderExtensions
{
/// <summary>
/// Start the host and listen on the specified urls.
/// Builds and starts the host.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to start.</param>
/// <returns>The <see cref="IHostBuilder"/>.</returns>
/// <returns>The started <see cref="IHost"/>.</returns>
public static IHost Start(this IHostBuilder hostBuilder)
{
return hostBuilder.StartAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Builds and starts the host.
/// </summary>
/// <param name="hostBuilder">The <see cref="IHostBuilder"/> to start.</param>
/// <param name="cancellationToken"></param>
/// <returns>The started <see cref="IHost"/>.</returns>
public static async Task<IHost> StartAsync(this IHostBuilder hostBuilder, CancellationToken cancellationToken = default)
{
var host = hostBuilder.Build();
host.StartAsync(CancellationToken.None).GetAwaiter().GetResult();
await host.StartAsync(cancellationToken);
return host;
}
}

View File

@ -6,6 +6,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.TestHost\Microsoft.AspNetCore.TestHost.csproj" />
<ProjectReference Include="..\..\src\Microsoft.Extensions.Hosting\Microsoft.Extensions.Hosting.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DiagnosticAdapter;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -23,6 +24,56 @@ namespace Microsoft.AspNetCore.TestHost
{
public class TestServerTests
{
[Fact]
public async Task GenericRawCreate()
{
var server = new TestServer();
var host = new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseServer(server)
.Configure(app => { });
})
.Build();
await host.StartAsync();
var response = await server.CreateClient().GetAsync("/");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GenericCreateAndStartHost_GetTestServer()
{
var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.Configure(app => { });
})
.StartAsync();
var response = await host.GetTestServer().CreateClient().GetAsync("/");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public async Task GenericCreateAndStartHost_GetTestClient()
{
var host = await new HostBuilder()
.ConfigureWebHost(webBuilder =>
{
webBuilder
.UseTestServer()
.Configure(app => { });
})
.StartAsync();
var response = await host.GetTestClient().GetAsync("/");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public void CreateWithDelegate()
{
@ -31,6 +82,17 @@ namespace Microsoft.AspNetCore.TestHost
new TestServer(new WebHostBuilder().Configure(app => { }));
}
[Fact]
public void CreateWithDelegate_DI()
{
var builder = new WebHostBuilder()
.Configure(app => { })
.UseTestServer();
var host = builder.Build();
host.Start();
}
[Fact]
public void DoesNotCaptureStartupErrorsByDefault()
{