// 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;
using System.IO;
using System.Net.Http;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
namespace Microsoft.AspNetCore.Mvc.Testing
{
///
/// Fixture for bootstrapping an application in memory for functional end to end tests.
///
/// The applications startup class.
public class WebApplicationTestFixture : IDisposable where TStartup : class
{
private readonly TestServer _server;
///
///
/// Creates a TestServer instance using the MVC application defined by.
/// The startup code defined in will be executed to configure the application.
///
///
/// This constructor will infer the application root directive by searching for a solution file (*.sln) and then
/// appending the path{AssemblyName} to the solution directory.The application root directory will be
/// used to discover views and content files.
///
///
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// .This means that project dependencies of the assembly containing
/// will be loaded as application assemblies.
///
///
public WebApplicationTestFixture()
: this(typeof(TStartup).Assembly.GetName().Name)
{
}
///
///
/// Creates a TestServer instance using the MVC application defined by.
/// The startup code defined in will be executed to configure the application.
///
///
/// This constructor will infer the application root directive by searching for a solution file (*.sln) and then
/// appending the path to the solution directory.The application root
/// directory will be used to discover views and content files.
///
///
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// .This means that project dependencies of the assembly containing
/// will be loaded as application assemblies.
///
///
/// The path to the project folder relative to the solution file of your
/// application. The folder of the first .sln file found traversing up the folder hierarchy from the test execution
/// folder is considered as the base path.
public WebApplicationTestFixture(string solutionRelativePath)
: this("*.sln", solutionRelativePath)
{
}
///
///
/// Creates a TestServer instance using the MVC application defined by.
/// The startup code defined in will be executed to configure the application.
///
///
/// This constructor will infer the application root directive by searching for a solution file that matches the pattern
/// and then appending the path
/// to the solution directory.The application root directory will be used to discover views and content files.
///
///
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// .This means that project dependencies of the assembly containing
/// will be loaded as application assemblies.
///
///
/// The glob pattern to use when searching for a solution file by
/// traversing up the folder hierarchy from the test execution folder.
/// The path to the project folder relative to the solution file of your
/// application. The folder of the first sln file that matches the
/// found traversing up the folder hierarchy from the test execution folder is considered as the base path.
public WebApplicationTestFixture(string solutionSearchPattern, string solutionRelativePath)
{
EnsureDepsFile();
var builder = CreateWebHostBuilder();
builder
.UseStartup()
.UseSolutionRelativeContentRoot(solutionRelativePath);
ConfigureWebHost(builder);
_server = CreateServer(builder);
Client = CreateClient();
}
private void EnsureDepsFile()
{
var depsFileName = $"{typeof(TStartup).Assembly.GetName().Name}.deps.json";
var depsFile = new FileInfo(Path.Combine(AppContext.BaseDirectory, depsFileName));
if (!depsFile.Exists)
{
throw new InvalidOperationException(Resources.FormatMissingDepsFile(
depsFile.FullName,
Path.GetFileName(depsFile.FullName)));
}
}
///
/// Creates a used to setup .
///
/// The default implementation of this method looks for a public static IWebHostBuilder CreateDefaultBuilder(string[] args)
/// method defined on the entry point of the assembly of and invokes it passing an empty string
/// array as arguments. In case this method can't be found,
///
///
/// A instance.
protected virtual IWebHostBuilder CreateWebHostBuilder() =>
WebHostBuilderFactory.CreateFromTypesAssemblyEntryPoint(Array.Empty()) ?? new WebHostBuilder();
///
/// Creates the with the bootstrapped application in .
///
/// The used to
/// create the server.
/// The with the bootstrapped application.
protected virtual TestServer CreateServer(IWebHostBuilder builder) => new TestServer(builder);
///
/// Gives a fixture an opportunity to configure the application before it gets built.
///
/// The for the application.
protected virtual void ConfigureWebHost(IWebHostBuilder builder)
{
}
///
/// Gets an instance of the used to send to the server.
///
public HttpClient Client { get; }
///
/// Creates a new instance of an that can be used to
/// send to the server.
///
/// The
public HttpClient CreateClient()
{
var client = _server.CreateClient();
client.BaseAddress = new Uri("http://localhost");
return client;
}
///
/// Creates a new instance of an that can be used to
/// send to the server.
///
/// The base address of the instance.
/// A list of instances to setup on the
/// .
/// The .
public HttpClient CreateClient(Uri baseAddress, params DelegatingHandler[] handlers)
{
if (handlers == null || handlers.Length == 0)
{
var client = _server.CreateClient();
client.BaseAddress = baseAddress;
return client;
}
else
{
for (var i = handlers.Length - 1; i > 1; i--)
{
handlers[i - 1].InnerHandler = handlers[i];
}
var serverHandler = _server.CreateHandler();
handlers[handlers.Length - 1].InnerHandler = serverHandler;
var client = new HttpClient(handlers[0]);
client.BaseAddress = baseAddress;
return client;
}
}
///
public void Dispose()
{
Client.Dispose();
_server.Dispose();
}
}
}