// 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(); } } }