Improvements to the MVC testing package

* Clean up unnecessary workarounds on the build project.
* Remove the need to specify the content root relative to the solution
  and use a solution based on an assembly level attribute on the test
  assembly created at build time.
* Remove non parameterless constructors.
* Add support for creating specialized factories from the base factory
  and keep track of "child" factories for disposal.
* Add support for creating clients that handle cookies and redirects
  automatically.
This commit is contained in:
Javier Calvarro Nelson 2018-01-16 12:30:56 -08:00
parent 869825b16c
commit 908e7a863b
107 changed files with 1309 additions and 440 deletions

View File

@ -32,7 +32,14 @@ namespace MvcSandbox
public static void Main(string[] args)
{
var host = new WebHostBuilder()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureLogging(factory =>
{
@ -42,11 +49,7 @@ namespace MvcSandbox
})
.UseIISIntegration()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
}
.UseStartup<Startup>();
}
}

View File

@ -0,0 +1,61 @@
// 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;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Testing.Handlers
{
/// <summary>
/// A <see cref="DelegatingHandler"/> that manages cookies associated with one or
/// more pairs of <see cref="HttpRequestMessage"/> and <see cref="HttpResponseMessage"/>.
/// </summary>
public class CookieContainerHandler : DelegatingHandler
{
/// <summary>
/// Creates a new instance of <see cref="CookieContainerHandler"/>.
/// </summary>
public CookieContainerHandler()
: this(new CookieContainer())
{
}
/// <summary>
/// Creates a new instance of <see cref="CookieContainerHandler"/>.
/// </summary>
/// <param name="cookieContainer">The <see cref="CookieContainer"/> to use for
/// storing and retrieving cookies.
/// </param>
public CookieContainerHandler(CookieContainer cookieContainer)
{
Container = cookieContainer;
}
/// <summary>
/// Gets the <see cref="CookieContainer"/> used to store and retrieve cookies.
/// </summary>
public CookieContainer Container { get; }
/// <inheritdoc />
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var cookieHeader = Container.GetCookieHeader(request.RequestUri);
request.Headers.Add(HeaderNames.Cookie, cookieHeader);
var response = await base.SendAsync(request, cancellationToken);
if (response.Headers.TryGetValues(HeaderNames.SetCookie, out var setCookieHeaders))
{
foreach (var header in setCookieHeaders)
{
Container.SetCookies(response.RequestMessage.RequestUri, header);
}
}
return response;
}
}
}

View File

@ -0,0 +1,161 @@
// 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;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Testing.Handlers
{
/// <summary>
/// A <see cref="DelegatingHandler"/> that follows redirect responses.
/// </summary>
public class RedirectHandler : DelegatingHandler
{
internal const int DefaultMaxRedirects = 7;
/// <summary>
/// Creates a new instance of <see cref="RedirectHandler"/>.
/// </summary>
public RedirectHandler()
: this(maxRedirects: DefaultMaxRedirects)
{
}
/// <summary>
/// Creates a new instance of <see cref="RedirectHandler"/>.
/// </summary>
/// <param name="maxRedirects">The maximun number of redirect responses to follow. It must be
/// equal or greater than 0.</param>
public RedirectHandler(int maxRedirects)
{
if (maxRedirects <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxRedirects));
}
MaxRedirects = maxRedirects;
}
/// <summary>
/// Gets the maximum number of redirects this handler will follow.
/// </summary>
public int MaxRedirects { get; }
/// <inheritdoc />
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var remainingRedirects = MaxRedirects;
var originalRequestContent = HasBody(request) ? await DuplicateRequestContent(request) : null;
var response = await base.SendAsync(request, cancellationToken);
while (IsRedirect(response) && remainingRedirects >= 0)
{
remainingRedirects--;
var redirectRequest = GetRedirectRequest(response, originalRequestContent);
originalRequestContent = HasBody(redirectRequest) ? await DuplicateRequestContent(redirectRequest) : null;
response = await base.SendAsync(redirectRequest, cancellationToken);
}
return response;
}
private static bool HasBody(HttpRequestMessage request) =>
request.Method == HttpMethod.Post || request.Method == HttpMethod.Put;
private static async Task<HttpContent> DuplicateRequestContent(HttpRequestMessage request)
{
if (request.Content == null)
{
return null;
}
var originalRequestContent = request.Content;
var (originalBody, copy) = await CopyBody(request);
var contentCopy = new StreamContent(copy);
request.Content = new StreamContent(originalBody);
CopyContentHeaders(originalRequestContent, request.Content, contentCopy);
return contentCopy;
}
private static void CopyContentHeaders(
HttpContent originalRequestContent,
HttpContent newRequestContent,
HttpContent contentCopy)
{
foreach (var header in originalRequestContent.Headers)
{
contentCopy.Headers.Add(header.Key, header.Value);
newRequestContent.Headers.Add(header.Key, header.Value);
}
}
private static async Task<(Stream originalBody, Stream copy)> CopyBody(HttpRequestMessage request)
{
var originalBody = await request.Content.ReadAsStreamAsync();
var bodyCopy = new MemoryStream();
await originalBody.CopyToAsync(bodyCopy);
bodyCopy.Seek(0, SeekOrigin.Begin);
if (originalBody.CanSeek)
{
originalBody.Seek(0, SeekOrigin.Begin);
}
else
{
originalBody = new MemoryStream();
await bodyCopy.CopyToAsync(originalBody);
originalBody.Seek(0, SeekOrigin.Begin);
bodyCopy.Seek(0, SeekOrigin.Begin);
}
return (originalBody, bodyCopy);
}
private static HttpRequestMessage GetRedirectRequest(
HttpResponseMessage response,
HttpContent originalContent)
{
var location = response.Headers.Location;
if (!location.IsAbsoluteUri)
{
location = new Uri(
new Uri(response.RequestMessage.RequestUri.GetLeftPart(UriPartial.Authority)),
location);
}
var redirect = !ShouldKeepVerb(response) ?
new HttpRequestMessage(HttpMethod.Get, location) :
new HttpRequestMessage(response.RequestMessage.Method, location)
{
Content = originalContent
};
foreach (var header in response.RequestMessage.Headers)
{
redirect.Headers.Add(header.Key, header.Value);
}
foreach (var property in response.RequestMessage.Properties)
{
redirect.Properties.Add(property.Key, property.Value);
}
return redirect;
}
private static bool ShouldKeepVerb(HttpResponseMessage response) =>
response.StatusCode == HttpStatusCode.RedirectKeepVerb ||
(int)response.StatusCode == 308;
private bool IsRedirect(HttpResponseMessage response) =>
response.StatusCode == HttpStatusCode.MovedPermanently ||
response.StatusCode == HttpStatusCode.Redirect ||
response.StatusCode == HttpStatusCode.RedirectKeepVerb ||
(int)response.StatusCode == 308;
}
}

View File

@ -10,6 +10,20 @@ namespace Microsoft.AspNetCore.Mvc.Testing
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNetCore.Mvc.Testing.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// No method 'public static {0} CreateWebHostBuilder(string[] args)' found on '{1}'. Alternatively, {2} can be extended and 'protected virtual {0} {3}()' can be overridden to provide your own {0} instance.
/// </summary>
internal static string MissingCreateWebHostBuilderMethod
{
get => GetString("MissingCreateWebHostBuilderMethod");
}
/// <summary>
/// No method 'public static {0} CreateWebHostBuilder(string[] args)' found on '{1}'. Alternatively, {2} can be extended and 'protected virtual {0} {3}()' can be overridden to provide your own {0} instance.
/// </summary>
internal static string FormatMissingCreateWebHostBuilderMethod(object p0, object p1, object p2, object p3)
=> string.Format(CultureInfo.CurrentCulture, GetString("MissingCreateWebHostBuilderMethod"), p0, p1, p2, p3);
/// <summary>
/// Can't find'{0}'. This file is required for functional tests to run properly. There should be a copy of the file on your source project bin folder. If that is not the case, make sure that the property PreserveCompilationContext is set to true on your project file. E.g '&lt;PreserveCompilationContext&gt;true&lt;/PreserveCompilationContext&gt;'. For functional tests to work they need to either run from the build output folder or the {1} file from your application's output directory must be copied to the folder where the tests are running on. A common cause for this error is having shadow copying enabled when the tests run.
/// </summary>

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="MissingCreateWebHostBuilderMethod" xml:space="preserve">
<value>No method 'public static {0} CreateWebHostBuilder(string[] args)' found on '{1}'. Alternatively, {2} can be extended and 'protected virtual {0} {3}()' can be overridden to provide your own {0} instance.</value>
</data>
<data name="MissingDepsFile" xml:space="preserve">
<value>Can't find'{0}'. This file is required for functional tests to run properly. There should be a copy of the file on your source project bin folder. If that is not the case, make sure that the property PreserveCompilationContext is set to true on your project file. E.g '&lt;PreserveCompilationContext&gt;true&lt;/PreserveCompilationContext&gt;'. For functional tests to work they need to either run from the build output folder or the {1} file from your application's output directory must be copied to the folder where the tests are running on. A common cause for this error is having shadow copying enabled when the tests run.</value>
</data>

View File

@ -0,0 +1,345 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyModel;
namespace Microsoft.AspNetCore.Mvc.Testing
{
/// <summary>
/// Factory for bootstrapping an application in memory for functional end to end tests.
/// </summary>
/// <typeparam name="TEntryPoint">A type in the entry point assembly of the application.
/// Typically the Startup or Program classes can be used.</typeparam>
public class WebApplicationFactory<TEntryPoint> : IDisposable where TEntryPoint : class
{
private TestServer _server;
private Action<IWebHostBuilder> _configuration;
private IList<HttpClient> _clients = new List<HttpClient>();
private List<WebApplicationFactory<TEntryPoint>> _derivedFactories =
new List<WebApplicationFactory<TEntryPoint>>();
/// <summary>
/// <para>
/// Creates an instance of <see cref="WebApplicationFactory{TEntryPoint}"/>. This factory can be used to
/// create a <see cref="TestServer"/> instance using the MVC application defined by <typeparamref name="TEntryPoint"/>
/// and one or more <see cref="HttpClient"/> instances used to send <see cref="HttpRequestMessage"/> to the <see cref="TestServer"/>.
/// The <see cref="WebApplicationFactory{TEntryPoint}"/> will find the entry point class of <typeparamref name="TEntryPoint"/>
/// assembly and initialize the application by calling <c>IWebHostBuilder CreateWebHostBuilder(string [] args)</c>
/// on <typeparamref name="TEntryPoint"/>.
/// </para>
/// <para>
/// This constructor will infer the application content root path by searching for a
/// <see cref="WebApplicationFactoryContentRootAttribute"/> on the assembly containing the functional tests with
/// a key equal to the <typeparamref name="TEntryPoint"/> assembly <see cref="Assembly.FullName"/>.
/// In case an attribute with the right key can't be found, <see cref="WebApplicationFactory{TEntryPoint}"/>
/// will fall back to searching for a solution file (*.sln) and then appending <typeparamref name="TEntryPoint"/> asembly name
/// to the solution directory. The application root directory will be used to discover views and content files.
/// </para>
/// <para>
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// <typeparamref name="TEntryPoint" />. This means that project dependencies of the assembly containing
/// <typeparamref name="TEntryPoint" /> will be loaded as application assemblies.
/// </para>
/// </summary>
public WebApplicationFactory() :
this(builder => { }, new WebApplicationFactoryClientOptions())
{
}
private WebApplicationFactory(Action<IWebHostBuilder> configuration, WebApplicationFactoryClientOptions options)
{
_configuration = configuration;
ClientOptions = options;
}
/// <summary>
/// Gets the <see cref="TestServer"/> created by this <see cref="WebApplicationFactory{TEntryPoint}"/>.
/// </summary>
public TestServer Server => _server;
/// <summary>
/// Gets the <see cref="IReadOnlyList{WebApplicationFactory}"/> of factories created from this factory
/// by further customizing the <see cref="IWebHostBuilder"/> when calling
/// <see cref="WebApplicationFactory{TEntryPoint}.WithWebHostBuilder(Action{IWebHostBuilder})"/>.
/// </summary>
public IReadOnlyList<WebApplicationFactory<TEntryPoint>> Factories => _derivedFactories.AsReadOnly();
/// <summary>
/// Gets the <see cref="WebApplicationFactoryClientOptions"/> used by <see cref="CreateClient()"/>.
/// </summary>
public WebApplicationFactoryClientOptions ClientOptions { get; }
/// <summary>
/// Creates a new <see cref="WebApplicationFactory{TEntryPoint}"/> with a <see cref="IWebHostBuilder"/>
/// that is further customized by <paramref name="configuration"/>.
/// </summary>
/// <param name="configuration">
/// An <see cref="Action{IWebHostBuilder}"/> to configure the <see cref="IWebHostBuilder"/>.
/// </param>
/// <returns>A new <see cref="WebApplicationFactory{TEntryPoint}"/>.</returns>
public WebApplicationFactory<TEntryPoint> WithWebHostBuilder(Action<IWebHostBuilder> configuration)
{
var factory = new WebApplicationFactory<TEntryPoint>(builder =>
{
_configuration(builder);
configuration(builder);
},
new WebApplicationFactoryClientOptions(ClientOptions));
_derivedFactories.Add(factory);
return factory;
}
private void EnsureServer()
{
if (_server != null)
{
return;
}
EnsureDepsFile();
var builder = CreateWebHostBuilder();
SetContentRoot(builder);
ConfigureWebHost(builder);
_configuration(builder);
_server = CreateServer(builder);
}
private void SetContentRoot(IWebHostBuilder builder)
{
var metadataAttributes = GetContentRootMetadataAttributes(
typeof(TEntryPoint).Assembly.FullName,
typeof(TEntryPoint).Assembly.GetName().Name);
string contentRoot = null;
for (var i = 0; i < metadataAttributes.Length; i++)
{
var contentRootAttribute = metadataAttributes[i];
var contentRootCandidate = Path.Combine(
AppContext.BaseDirectory,
contentRootAttribute.ContentRootPath);
var contentRootMarker = Path.Combine(
contentRootCandidate,
Path.GetFileName(contentRootAttribute.ContentRootTest));
if (File.Exists(contentRootMarker))
{
contentRoot = contentRootCandidate;
break;
}
}
if (contentRoot != null)
{
builder.UseContentRoot(contentRoot);
}
else
{
builder.UseSolutionRelativeContentRoot(typeof(TEntryPoint).Assembly.GetName().Name);
}
}
private WebApplicationFactoryContentRootAttribute[] GetContentRootMetadataAttributes(
string tEntryPointAssemblyFullName,
string tEntryPointAssemblyName)
{
var testAssembly = GetTestAssemblies();
var metadataAttributes = testAssembly
.SelectMany(a => a.GetCustomAttributes<WebApplicationFactoryContentRootAttribute>())
.Where(a => string.Equals(a.Key, tEntryPointAssemblyFullName, StringComparison.OrdinalIgnoreCase) ||
string.Equals(a.Key, tEntryPointAssemblyName, StringComparison.OrdinalIgnoreCase))
.OrderBy(a => a.Priority)
.ToArray();
return metadataAttributes;
}
/// <summary>
/// Gets the assemblies containing the functional tests. The
/// <see cref="WebApplicationFactoryContentRootAttribute"/> applied to these
/// assemblies defines the content root to use for the given
/// <typeparamref name="TEntryPoint"/>.
/// </summary>
/// <returns>The list of <see cref="Assembly"/> containing tests.</returns>
protected virtual IEnumerable<Assembly> GetTestAssemblies()
{
try
{
// The default dependency context will be populated in .net core applications.
var context = DependencyContext.Default;
if (context != null)
{
// Find the list of projects
var projects = context.CompileLibraries.Where(l => l.Type == "project");
// Find the list of projects runtime information and their assembly names.
var runtimeProjectLibraries = context.RuntimeLibraries
.Where(r => projects.Any(p => p.Name == r.Name))
.ToDictionary(r => r, r => r.GetDefaultAssemblyNames(context).ToArray());
var entryPointAssemblyName = typeof(TEntryPoint).Assembly.GetName().Name;
// Find the project containing TEntryPoint
var entryPointRuntimeLibrary = runtimeProjectLibraries
.Single(rpl => rpl.Value.Any(a => string.Equals(a.Name, entryPointAssemblyName, StringComparison.Ordinal)));
// Find the list of projects referencing TEntryPoint.
var candidates = runtimeProjectLibraries
.Where(rpl => rpl.Key.Dependencies
.Any(d => string.Equals(d.Name, entryPointRuntimeLibrary.Key.Name, StringComparison.Ordinal)));
return candidates.SelectMany(rl => rl.Value).Select(Assembly.Load);
}
else
{
// The app domain friendly name will be populated in full framework.
return new[] { Assembly.Load(AppDomain.CurrentDomain.FriendlyName) };
}
}
catch (Exception)
{
}
return Array.Empty<Assembly>();
}
private void EnsureDepsFile()
{
var depsFileName = $"{typeof(TEntryPoint).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)));
}
}
/// <summary>
/// Creates a <see cref="IWebHostBuilder"/> used to set up <see cref="TestServer"/>.
/// </summary>
/// <remarks>
/// The default implementation of this method looks for a <c>public static IWebHostBuilder CreateDefaultBuilder(string[] args)</c>
/// method defined on the entry point of the assembly of <typeparamref name="TEntryPoint" /> and invokes it passing an empty string
/// array as arguments.
/// </remarks>
/// <returns>A <see cref="IWebHostBuilder"/> instance.</returns>
protected virtual IWebHostBuilder CreateWebHostBuilder() =>
WebHostBuilderFactory.CreateFromTypesAssemblyEntryPoint<TEntryPoint>(Array.Empty<string>()) ??
throw new InvalidOperationException(Resources.FormatMissingCreateWebHostBuilderMethod(
nameof(IWebHostBuilder),
typeof(TEntryPoint).Assembly.EntryPoint.DeclaringType.FullName,
typeof(WebApplicationFactory<TEntryPoint>).Name,
nameof(CreateWebHostBuilder)));
/// <summary>
/// Creates the <see cref="TestServer"/> with the bootstrapped application in <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IWebHostBuilder"/> used to
/// create the server.</param>
/// <returns>The <see cref="TestServer"/> with the bootstrapped application.</returns>
protected virtual TestServer CreateServer(IWebHostBuilder builder) => new TestServer(builder);
/// <summary>
/// Gives a fixture an opportunity to configure the application before it gets built.
/// </summary>
/// <param name="builder">The <see cref="IWebHostBuilder"/> for the application.</param>
protected virtual void ConfigureWebHost(IWebHostBuilder builder)
{
}
/// <summary>
/// Creates an instance of <see cref="HttpClient"/> that automatically follows
/// redirects and handles cookies.
/// </summary>
/// <returns>The <see cref="HttpClient"/>.</returns>
public HttpClient CreateClient() =>
CreateClient(ClientOptions);
/// <summary>
/// Creates an instance of <see cref="HttpClient"/> that automatically follows
/// redirects and handles cookies.
/// </summary>
/// <returns>The <see cref="HttpClient"/>.</returns>
public HttpClient CreateClient(WebApplicationFactoryClientOptions options) =>
CreateDefaultClient(options.BaseAddress, options.CreateHandlers());
/// <summary>
/// Creates a new instance of an <see cref="HttpClient"/> that can be used to
/// send <see cref="HttpRequestMessage"/> to the server. The base address of the <see cref="HttpClient"/>
/// instance will be set to <c>http://localhost</c>.
/// </summary>
/// <param name="handlers">A list of <see cref="DelegatingHandler"/> instances to set up on the
/// <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="HttpClient"/>.</returns>
public HttpClient CreateDefaultClient(params DelegatingHandler[] handlers) =>
CreateDefaultClient(new Uri("http://localhost"), handlers);
/// <summary>
/// Creates a new instance of an <see cref="HttpClient"/> that can be used to
/// send <see cref="HttpRequestMessage"/> to the server.
/// </summary>
/// <param name="baseAddress">The base address of the <see cref="HttpClient"/> instance.</param>
/// <param name="handlers">A list of <see cref="DelegatingHandler"/> instances to set up on the
/// <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="HttpClient"/>.</returns>
public HttpClient CreateDefaultClient(Uri baseAddress, params DelegatingHandler[] handlers)
{
EnsureServer();
if (handlers == null || handlers.Length == 0)
{
var client = _server.CreateClient();
client.BaseAddress = baseAddress;
return client;
}
else
{
for (var i = handlers.Length - 1; i > 0; i--)
{
handlers[i - 1].InnerHandler = handlers[i];
}
var serverHandler = _server.CreateHandler();
handlers[handlers.Length - 1].InnerHandler = serverHandler;
var client = new HttpClient(handlers[0])
{
BaseAddress = baseAddress
};
_clients.Add(client);
return client;
}
}
/// <inheritdoc />
public void Dispose()
{
foreach (var client in _clients)
{
client.Dispose();
}
foreach (var factory in _derivedFactories)
{
factory.Dispose();
}
_server?.Dispose();
}
}
}

View File

@ -0,0 +1,83 @@
// 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.Collections.Generic;
using System.Linq;
using System.Net.Http;
using Microsoft.AspNetCore.Mvc.Testing.Handlers;
namespace Microsoft.AspNetCore.Mvc.Testing
{
/// <summary>
/// The default options to use to when creating
/// <see cref="HttpClient"/> instances by calling
/// <see cref="WebApplicationFactory{TEntryPoint}.CreateClient(WebApplicationFactoryClientOptions)"/>.
/// </summary>
public class WebApplicationFactoryClientOptions
{
/// <summary>
/// Initializes a new instance of <see cref="WebApplicationFactoryClientOptions"/>.
/// </summary>
public WebApplicationFactoryClientOptions()
{
}
// Copy constructor
internal WebApplicationFactoryClientOptions(WebApplicationFactoryClientOptions clientOptions)
{
BaseAddress = clientOptions.BaseAddress;
AllowAutoRedirect = clientOptions.AllowAutoRedirect;
MaxAutomaticRedirections = clientOptions.MaxAutomaticRedirections;
HandleCookies = clientOptions.HandleCookies;
}
/// <summary>
/// Gets or sets the base address of <see cref="HttpClient"/> instances created by calling
/// <see cref="WebApplicationFactory{TEntryPoint}.CreateClient(WebApplicationFactoryClientOptions)"/>.
/// The default is <c>http://localhost</c>.
/// </summary>
public Uri BaseAddress { get; set; } = new Uri("http://localhost");
/// <summary>
/// Gets or sets whether or not <see cref="HttpClient"/> instances created by calling
/// <see cref="WebApplicationFactory{TEntryPoint}.CreateClient(WebApplicationFactoryClientOptions)"/>
/// should automatically follow redirect responses.
/// The default is <c>true</c>.
/// /// </summary>
public bool AllowAutoRedirect { get; set; } = true;
/// <summary>
/// Gets or sets the maximum number of redirect responses that <see cref="HttpClient"/> instances
/// created by calling <see cref="WebApplicationFactory{TEntryPoint}.CreateClient(WebApplicationFactoryClientOptions)"/>
/// should follow.
/// The default is <c>7</c>.
/// </summary>
public int MaxAutomaticRedirections { get; set; } = RedirectHandler.DefaultMaxRedirects;
/// <summary>
/// Gets or sets whether <see cref="HttpClient"/> instances created by calling
/// <see cref="WebApplicationFactory{TEntryPoint}.CreateClient(WebApplicationFactoryClientOptions)"/>
/// should handle cookies.
/// The default is <c>true</c>.
/// </summary>
public bool HandleCookies { get; set; } = true;
internal DelegatingHandler[] CreateHandlers()
{
return CreateHandlersCore().ToArray();
IEnumerable<DelegatingHandler> CreateHandlersCore()
{
if (AllowAutoRedirect)
{
yield return new RedirectHandler(MaxAutomaticRedirections);
}
if (HandleCookies)
{
yield return new CookieContainerHandler();
}
}
}
}
}

View File

@ -0,0 +1,83 @@
// 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.Globalization;
using System.IO;
using System.Reflection;
namespace Microsoft.AspNetCore.Mvc.Testing
{
/// <summary>
/// Metadata that <see cref="WebApplicationFactory{TEntryPoint}"/> uses to find out the content
/// root for the web application represented by <c>TEntryPoint</c>.
/// <see cref="WebApplicationFactory{TEntryPoint}"/> will iterate over all the instances of
/// <see cref="WebApplicationFactoryContentRootAttribute"/>, filter the instances whose
/// <see cref="Key"/> is equal to <c>TEntryPoint</c> <see cref="Assembly.FullName"/>,
/// order them by <see cref="Priority"/> in ascending order.
/// <see cref="WebApplicationFactory{TEntryPoint}"/> will check for the existence of the marker
/// in <code>Path.Combine(<see cref="ContentRootPath"/>, Path.GetFileName(<see cref="ContentRootTest"/>))"</code>
/// and if the file exists it will set the content root to <see cref="ContentRootPath"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)]
public sealed class WebApplicationFactoryContentRootAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of <see cref="WebApplicationFactoryContentRootAttribute"/>.
/// </summary>
/// <param name="key">
/// The key of this <see cref="WebApplicationFactoryContentRootAttribute"/>. This
/// key is used by <see cref="WebApplicationFactory{TEntryPoint}"/> to determine what of the
/// <see cref="WebApplicationFactoryContentRootAttribute"/> instances on the test assembly should be used
/// to match a given TEntryPoint class.
/// </param>
/// <param name="contentRootPath">The path to the content root. This path can be either relative or absolute.
/// In case the path is relative, the path will be combined with
/// <code><see cref="Directory.GetCurrentDirectory()"/></code></param>
/// <param name="contentRootTest">
/// A file that will be use as a marker to determine that the content root path for the given context is correct.
/// </param>
/// <param name="priority">
/// The priority of this content root attribute compared to other attributes. When
/// multiple <see cref="WebApplicationFactoryContentRootAttribute"/> instances are applied for the
/// same key, they are processed with <see cref="int.Parse(string)"/>, ordered in ascending order and applied
/// in priority until a match is found.
/// </param>
public WebApplicationFactoryContentRootAttribute(
string key,
string contentRootPath,
string contentRootTest,
string priority)
{
Key = key;
ContentRootPath = contentRootPath;
ContentRootTest = contentRootTest;
if (int.TryParse(priority, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedPriority))
{
Priority = parsedPriority;
}
}
/// <summary>
/// Gets the key for the content root associated with this project. Typically <see cref="Assembly.FullName"/>.
/// </summary>
public string Key { get; }
/// <summary>
/// Gets the content root path for a given project. This content root can be relative or absolute. If it is a
/// relative path, it will be combined with <see cref="AppContext.BaseDirectory"/>.
/// </summary>
public string ContentRootPath { get; }
/// <summary>
/// A marker file used to ensure that the path the content root is being set to is correct.
/// </summary>
public string ContentRootTest { get; }
/// <summary>
/// Gets a number for determining the probing order when multiple <see cref="WebApplicationFactoryContentRootAttribute"/>
/// instances with the same key are present on the test <see cref="Assembly"/>.
/// </summary>
public int Priority { get; }
}
}

View File

@ -1,200 +0,0 @@
// 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
{
/// <summary>
/// Fixture for bootstrapping an application in memory for functional end to end tests.
/// </summary>
/// <typeparam name="TStartup">The applications startup class.</typeparam>
public class WebApplicationTestFixture<TStartup> : IDisposable where TStartup : class
{
private readonly TestServer _server;
/// <summary>
/// <para>
/// Creates a TestServer instance using the MVC application defined by<typeparamref name="TStartup"/>.
/// The startup code defined in <typeparamref name = "TStartup" /> will be executed to configure the application.
/// </para>
/// <para>
/// This constructor will infer the application root directive by searching for a solution file (*.sln) and then
/// appending the path<c>{AssemblyName}</c> to the solution directory.The application root directory will be
/// used to discover views and content files.
/// </para>
/// <para>
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// <typeparamref name = "TStartup" />.This means that project dependencies of the assembly containing
/// <typeparamref name = "TStartup" /> will be loaded as application assemblies.
/// </para>
/// </summary>
public WebApplicationTestFixture()
: this(typeof(TStartup).Assembly.GetName().Name)
{
}
/// <summary>
/// <para>
/// Creates a TestServer instance using the MVC application defined by<typeparamref name="TStartup"/>.
/// The startup code defined in <typeparamref name = "TStartup" /> will be executed to configure the application.
/// </para>
/// <para>
/// This constructor will infer the application root directive by searching for a solution file (*.sln) and then
/// appending the path <paramref name="solutionRelativePath"/> to the solution directory.The application root
/// directory will be used to discover views and content files.
/// </para>
/// <para>
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// <typeparamref name = "TStartup" />.This means that project dependencies of the assembly containing
/// <typeparamref name = "TStartup" /> will be loaded as application assemblies.
/// </para>
/// </summary>
/// <param name="solutionRelativePath">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.</param>
protected WebApplicationTestFixture(string solutionRelativePath)
: this("*.sln", solutionRelativePath)
{
}
/// <summary>
/// <para>
/// Creates a TestServer instance using the MVC application defined by<typeparamref name="TStartup"/>.
/// The startup code defined in <typeparamref name = "TStartup" /> will be executed to configure the application.
/// </para>
/// <para>
/// This constructor will infer the application root directive by searching for a solution file that matches the pattern
/// <paramref name="solutionSearchPattern"/> and then appending the path <paramref name="solutionRelativePath"/>
/// to the solution directory.The application root directory will be used to discover views and content files.
/// </para>
/// <para>
/// The application assemblies will be loaded from the dependency context of the assembly containing
/// <typeparamref name = "TStartup" />.This means that project dependencies of the assembly containing
/// <typeparamref name = "TStartup" /> will be loaded as application assemblies.
/// </para>
/// </summary>
/// <param name="solutionSearchPattern">The glob pattern to use when searching for a solution file by
/// traversing up the folder hierarchy from the test execution folder.</param>
/// <param name="solutionRelativePath">The path to the project folder relative to the solution file of your
/// application. The folder of the first sln file that matches the <paramref name="solutionSearchPattern"/>
/// found traversing up the folder hierarchy from the test execution folder is considered as the base path.</param>
protected WebApplicationTestFixture(string solutionSearchPattern, string solutionRelativePath)
{
EnsureDepsFile();
var builder = CreateWebHostBuilder();
builder
.UseStartup<TStartup>()
.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)));
}
}
/// <summary>
/// Creates a <see cref="IWebHostBuilder"/> used to setup <see cref="TestServer"/>.
/// <remarks>
/// The default implementation of this method looks for a <c>public static IWebHostBuilder CreateDefaultBuilder(string[] args)</c>
/// method defined on the entry point of the assembly of <typeparamref name="TStartup" /> and invokes it passing an empty string
/// array as arguments. In case this method can't be found,
/// </remarks>
/// </summary>
/// <returns>A <see cref="IWebHostBuilder"/> instance.</returns>
protected virtual IWebHostBuilder CreateWebHostBuilder() =>
WebHostBuilderFactory.CreateFromTypesAssemblyEntryPoint<TStartup>(Array.Empty<string>()) ?? new WebHostBuilder();
/// <summary>
/// Creates the <see cref="TestServer"/> with the bootstrapped application in <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IWebHostBuilder"/> used to
/// create the server.</param>
/// <returns>The <see cref="TestServer"/> with the bootstrapped application.</returns>
protected virtual TestServer CreateServer(IWebHostBuilder builder) => new TestServer(builder);
/// <summary>
/// Gives a fixture an opportunity to configure the application before it gets built.
/// </summary>
/// <param name="builder">The <see cref="IWebHostBuilder"/> for the application.</param>
protected virtual void ConfigureWebHost(IWebHostBuilder builder)
{
}
/// <summary>
/// Gets an instance of the <see cref="HttpClient"/> used to send <see cref="HttpRequestMessage"/> to the server.
/// </summary>
public HttpClient Client { get; }
/// <summary>
/// Creates a new instance of an <see cref="HttpClient"/> that can be used to
/// send <see cref="HttpRequestMessage"/> to the server.
/// </summary>
/// <returns>The <see cref="HttpClient"/></returns>
public HttpClient CreateClient()
{
var client = _server.CreateClient();
client.BaseAddress = new Uri("http://localhost");
return client;
}
/// <summary>
/// Creates a new instance of an <see cref="HttpClient"/> that can be used to
/// send <see cref="HttpRequestMessage"/> to the server.
/// </summary>
/// <param name="baseAddress">The base address of the <see cref="HttpClient"/> instance.</param>
/// <param name="handlers">A list of <see cref="DelegatingHandler"/> instances to setup on the
/// <see cref="HttpClient"/>.</param>
/// <returns>The <see cref="HttpClient"/>.</returns>
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;
}
}
/// <inheritdoc />
public void Dispose()
{
Client.Dispose();
_server.Dispose();
}
}
}

View File

@ -15,11 +15,43 @@
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>
<Target Name="CopyAditionalFiles" AfterTargets="Build" Condition="'$(TargetFramework)'!=''">
<Target Name="_ResolveMvcTestProjectReferences" DependsOnTargets="ResolveReferences">
<ItemGroup>
<_ContentRootProjectReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.ReferenceSourceTarget)' == 'ProjectReference'" />
</ItemGroup>
</Target>
<Target Name="_AddContentRootForProjectReferences" BeforeTargets="BeforeCompile" DependsOnTargets="_ResolveMvcTestProjectReferences">
<ItemGroup>
<DepsFilePaths Include="$([System.IO.Path]::ChangeExtension('%(_ResolvedProjectReferencePaths.FullPath)', '.deps.json'))" />
<WebApplicationFactoryContentRootAttribute
Condition="'%(_ContentRootProjectReferences.Identity)' != ''"
Include="%(_ContentRootProjectReferences.Identity)"
AssemblyName="%(_ContentRootProjectReferences.FusionName)"
ContentRootPath="$([System.IO.Path]::GetDirectoryName(%(_ContentRootProjectReferences.MSBuildSourceProjectFile)))"
ContentRootTest="$([System.IO.Path]::GetFileName(%(_ContentRootProjectReferences.MSBuildSourceProjectFile)))"
Priority="0" />
</ItemGroup>
<ItemGroup>
<AssemblyAttribute
Condition=" '%(WebApplicationFactoryContentRootAttribute.Identity)' != '' "
Include="Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryContentRootAttribute">
<_Parameter1>%(WebApplicationFactoryContentRootAttribute.AssemblyName)</_Parameter1>
<_Parameter2>%(WebApplicationFactoryContentRootAttribute.ContentRootPath)</_Parameter2>
<_Parameter3>%(WebApplicationFactoryContentRootAttribute.ContentRootTest)</_Parameter3>
<_Parameter4>%(WebApplicationFactoryContentRootAttribute.Priority)</_Parameter4>
</AssemblyAttribute>
</ItemGroup>
</Target>
<Target Name="CopyAditionalFiles" AfterTargets="Build;_ResolveMvcTestProjectReferences" Condition="'$(TargetFramework)'!=''">
<ItemGroup>
<DepsFilePaths
Condition="'%(_ContentRootProjectReferences.Identity)' != ''"
Include="$([System.IO.Path]::ChangeExtension('%(_ContentRootProjectReferences.ResolvedFrom)', '.deps.json'))" />
</ItemGroup>
<Copy SourceFiles="%(DepsFilePaths.FullPath)" DestinationFolder="$(OutputPath)" Condition="Exists('%(DepsFilePaths.FullPath)')" />
</Target>
</Project>

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public AntiforgeryAuthTests(MvcTestFixture<Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public AntiforgeryTests(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ApiBehaviorTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ApiExplorerTest(MvcTestFixture<ApiExplorerWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ApplicationModelTest(MvcTestFixture<ApplicationModelWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public AsyncActionsTests(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public BasicTests(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public CombineAuthorizeTests(MvcTestFixture<StartupWithGlobalAuthorizeAndAllowCombiningAuthorizeFilters> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public CompilationOptionsTests(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ConsumesAttributeTests(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ContentNegotiationTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ControllerFromServicesTest(MvcTestFixture<ControllersFromServicesWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public CorsTests(MvcTestFixture<CorsWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public DefaultOrderTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public DefaultValuesTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public DirectivesTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public DoNotRespectBrowserAcceptHeaderTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
"'Microsoft.NET.Sdk.Web' and the 'PreserveCompilationContext' property is not set to false.");
public ErrorPageTests(MvcTestFixture<ErrorPageMiddlewareWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public FileResultTests(MvcTestFixture<FilesWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public FiltersTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public FlushPointTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public FormFileUploadTest(MvcTestFixture<FilesWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -5,6 +5,8 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -13,9 +15,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public GlobalAuthorizationFilterTest(MvcTestFixture<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<SecurityWebSite.StartupWithGlobalDenyAnonymousFilter>();
public HttpClient Client { get; }
[Fact]
@ -66,7 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal(
"http://localhost/Home/Login?ReturnUrl=%2FAdministration%2FEitherCookie",
response.Headers.Location.ToString());
}
}
}
}

View File

@ -23,8 +23,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
MvcTestFixture<HtmlGenerationWebSite.Startup> fixture,
MvcEncodedTestFixture<HtmlGenerationWebSite.Startup> encodedFixture)
{
Client = fixture.Client;
EncodedClient = encodedFixture.Client;
Client = fixture.CreateDefaultClient();
EncodedClient = encodedFixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public HtmlHelperOptionsTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -1,13 +0,0 @@
// 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.IO;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class MvcSampleFixture<TStartup> : MvcTestFixture<TStartup>
where TStartup : class
{
public MvcSampleFixture() : base(Path.Combine("samples", typeof(TStartup).Assembly.GetName().Name)) { }
}
}

View File

@ -1,6 +1,7 @@
// 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.Globalization;
using System.IO;
using Microsoft.AspNetCore.Hosting;
@ -9,19 +10,9 @@ using Microsoft.AspNetCore.TestHost;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class MvcTestFixture<TStartup> : WebApplicationTestFixture<TStartup>
public class MvcTestFixture<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
public MvcTestFixture()
: base(Path.Combine("test", "WebSites", typeof(TStartup).Assembly.GetName().Name))
{
}
protected MvcTestFixture(string solutionRelativePath)
: base(solutionRelativePath)
{
}
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
builder.UseRequestCulture<TStartup>("en-GB", "en-US");

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public InputFormatterTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public InputObjectValidationTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public InputValidationTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public JsonOutputFormatterTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public JsonPatchSampleTest(MvcTestFixture<Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public JsonResultTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public LinkGenerationTests(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -16,6 +16,24 @@
<None Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<!-- For testing the testing infrastructure only -->
<WebApplicationFactoryContentRootAttribute
Include="BasicWebSite"
AssemblyName="BasicWebsite"
ContentRootPath="../../../../WebSites/BasicWebSite"
ContentRootTest="BasicWebSite.csproj"
Priority="-1" />
<!-- For testing the testing infrastructure only. This attribute is
incorrect by design to ensure that the infrastructure falls back. -->
<WebApplicationFactoryContentRootAttribute
Include="FiltersWebSite"
AssemblyName="FiltersWebSite"
ContentRootPath="/../WebSites/FiltersWebSite"
ContentRootTest="Incorrect.csproj"
Priority="-1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Mvc.Testing\Microsoft.AspNetCore.Mvc.Testing.csproj" />
<ProjectReference Include="..\WebSites\ApiExplorerWebSite\ApiExplorerWebSite.csproj" />

View File

@ -7,11 +7,11 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class MvcSandboxTest : IClassFixture<MvcSampleFixture<MvcSandbox.Startup>>
public class MvcSandboxTest : IClassFixture<MvcTestFixture<MvcSandbox.Startup>>
{
public MvcSandboxTest(MvcSampleFixture<MvcSandbox.Startup> fixture)
public MvcSandboxTest(MvcTestFixture<MvcSandbox.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public OutputFormatterTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RazorBuildTest(MvcTestFixture<RazorBuildWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public RazorPageExecutionInstrumentationTest(MvcTestFixture<Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -13,9 +15,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RazorPageModelTest(MvcTestFixture<RazorPagesWebSite.Startup> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<RazorPagesWebSite.Startup>();
public HttpClient Client { get; }
[Fact]

View File

@ -1,12 +1,10 @@
// 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.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -15,9 +13,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RazorPagesNamespaceTest(MvcTestFixture<RazorPagesWebSite.Startup> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<RazorPagesWebSite.Startup>();
public HttpClient Client { get; }
[Fact]

View File

@ -9,6 +9,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -19,9 +20,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public RazorPagesTest(MvcTestFixture<RazorPagesWebSite.Startup> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<RazorPagesWebSite.Startup>();
public HttpClient Client { get; }
[Fact]

View File

@ -1,12 +1,10 @@
// 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.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -15,9 +13,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RazorPagesViewSearchTest(MvcTestFixture<RazorPagesWebSite.Startup> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<RazorPagesWebSite.Startup>();
public HttpClient Client { get; }
[Fact]

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RazorPagesWithBasePathTest(MvcTestFixture<RazorPagesWebSite.StartupWithBasePath> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public RazorViewLocationSpecificationTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public RemoteAttributeValidationTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -2,10 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -14,9 +16,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RequestFormLimitsTest(MvcTestFixture<BasicWebSite.StartupRequestLimitSize> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<BasicWebSite.StartupRequestLimitSize>();
public HttpClient Client { get; }
[Fact]
@ -66,7 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var result = await response.Content.ReadAsStringAsync();
Assert.Equal(expected, result);
}
[Fact]
public async Task OverrideControllerLevelLimits_UsingDefaultLimits()
{

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RequestServicesTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -3,12 +3,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -23,9 +25,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public RequestSizeLimitTest(MvcTestFixture<BasicWebSite.StartupRequestLimitSize> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<BasicWebSite.StartupRequestLimitSize>();
public HttpClient Client { get; }
[Fact]

View File

@ -1,9 +1,11 @@
// 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.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -15,9 +17,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RespectBrowserAcceptHeaderTests(MvcTestFixture<FormatterWebSite.StartupWithRespectBrowserAcceptHeader> fixture)
{
Client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<FormatterWebSite.StartupWithRespectBrowserAcceptHeader>();
public HttpClient Client { get; }
[Fact]

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RouteDataTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public RoutingTests(MvcTestFixture<RoutingWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public SerializableErrorTests(MvcTestFixture<XmlFormattersWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public SimpleTests(MvcTestFixture<SimpleWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public StreamOutputFormatterTest(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public TagHelperComponentTagHelperTest(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public TagHelpersFromServicesTest(MvcTestFixture<ControllersFromServicesWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -25,8 +25,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
MvcTestFixture<TagHelpersWebSite.Startup> fixture,
MvcEncodedTestFixture<TagHelpersWebSite.Startup> encodedFixture)
{
Client = fixture.Client;
EncodedClient = encodedFixture.Client;
Client = fixture.CreateDefaultClient();
EncodedClient = encodedFixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public TempDataInCookiesTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
protected override HttpClient Client { get; }

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Net.Http.Headers;
using Xunit;
@ -20,9 +21,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public TempDataInCookiesUsingCookieConsentTest(
MvcTestFixture<BasicWebSite.StartupWithCookieTempDataProviderAndCookieConsent> fixture)
{
_client = fixture.Client;
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
_client = factory.CreateDefaultClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.UseStartup<BasicWebSite.StartupWithCookieTempDataProviderAndCookieConsent>();
[Fact]
public async Task CookieTempDataProviderCookie_SetInResponse_OnGrantingConsent()
{

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public TempDataInSessionTest(MvcTestFixture<BasicWebSite.StartupWithSessionTempDataProvider> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
protected override HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public TempDataPropertyTest(MvcTestFixture<BasicWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
[Fact]

View File

@ -0,0 +1,97 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using BasicWebSite;
using BasicWebSite.Controllers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class TestingInfrastructureTests : IClassFixture<WebApplicationFactory<BasicWebSite.Startup>>
{
public TestingInfrastructureTests(WebApplicationFactory<BasicWebSite.Startup> fixture)
{
var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder);
Client = factory.CreateClient();
}
private static void ConfigureWebHostBuilder(IWebHostBuilder builder) =>
builder.ConfigureTestServices(s => s.AddSingleton<TestService, OverridenService>());
public HttpClient Client { get; }
[Fact]
public async Task TestingInfrastructure_CanOverrideServiceFromWithinTheTest()
{
// Act
var response = await Client.GetStringAsync("Testing/Builder");
// Assert
Assert.Equal("Test", response);
}
[Fact]
public async Task TestingInfrastructure_RedirectHandlerWorksWithPreserveMethod()
{
// Act
var request = new HttpRequestMessage(HttpMethod.Post, "Testing/RedirectHandler/2")
{
Content = new ObjectContent<Number>(new Number { Value = 5 }, new JsonMediaTypeFormatter())
};
request.Headers.Add("X-Pass-Thru", "Some-Value");
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var xPassThruValue = Assert.Single(response.Headers.GetValues("X-Pass-Thru"));
Assert.Equal("Some-Value", xPassThruValue);
var handlerResponse = await response.Content.ReadAsAsync<RedirectHandlerResponse>();
Assert.Equal(5, handlerResponse.Url);
Assert.Equal(5, handlerResponse.Body);
}
[Fact]
public async Task TestingInfrastructure_PostRedirectGetWorksWithCookies()
{
// Act
var acquireToken = await Client.GetAsync("Testing/AntiforgerySimulator/3");
Assert.Equal(HttpStatusCode.OK, acquireToken.StatusCode);
var response = await Client.PostAsync(
"Testing/PostRedirectGet/Post/3",
content: null);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var handlerResponse = await response.Content.ReadAsAsync<PostRedirectGetGetResponse>();
Assert.Equal(4, handlerResponse.TempDataValue);
Assert.Equal("Value-4", handlerResponse.CookieValue);
}
[Fact]
public async Task TestingInfrastructure_PutWithoutBodyFollowsRedirects()
{
// Act
var response = await Client.PutAsync("Testing/Put/3", content: null);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(5, await response.Content.ReadAsAsync<int>());
}
private class OverridenService : TestService
{
public OverridenService()
{
Message = "Test";
}
}
}
}

View File

@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
MvcTestFixture<RazorWebSite.Startup> fixture,
MvcEncodedTestFixture<RazorWebSite.Startup> encodedFixture)
{
Client = fixture.Client;
EncodedClient = encodedFixture.Client;
Client = fixture.CreateDefaultClient();
EncodedClient = encodedFixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public VersioningTests(MvcTestFixture<VersioningWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public ViewComponentFromServicesTest(MvcTestFixture<ControllersFromServicesWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public ViewEngineTests(MvcTestFixture<RazorWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public WebApiCompatShimActionResultTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public WebApiCompatShimActionSelectionTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public WebApiCompatShimBasicTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public WebApiCompatShimParameterBindingTest(MvcTestFixture<WebApiCompatShimWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public XmlDataContractSerializerFormattersWrappingTest(MvcTestFixture<XmlFormattersWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
public XmlDataContractSerializerInputFormatterTest(MvcTestFixture<Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public XmlOutputFormatterTests(MvcTestFixture<FormatterWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public XmlSerializerFormattersWrappingTest(MvcTestFixture<XmlFormattersWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public XmlSerializerInputFormatterTests(MvcTestFixture<XmlFormattersWebSite.Startup> fixture)
{
Client = fixture.Client;
Client = fixture.CreateDefaultClient();
}
public HttpClient Client { get; }

View File

@ -51,15 +51,18 @@ namespace ApiExplorerWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseIISIntegration()
.UseStartup<Startup>();
}
}

View File

@ -38,15 +38,18 @@ namespace ApplicationModelWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace BasicWebSite.Controllers
{
public class TestingController
public class TestingController : Controller
{
public TestingController(TestService service)
{
@ -17,5 +18,94 @@ namespace BasicWebSite.Controllers
[HttpGet("Testing/Builder")]
public string Get() => Service.Message;
[HttpPost("Testing/RedirectHandler/{value}")]
public IActionResult RedirectHandler(
[FromRoute]int value,
[FromBody] Number number,
[FromHeader(Name = "X-Pass-Thru")] string passThruValue)
{
Response.Headers.Add("X-Pass-Thru", passThruValue);
if (value < number.Value)
{
return RedirectToActionPreserveMethod(
nameof(RedirectHandler),
"Testing",
new { value = value + 1 });
}
return Ok(new RedirectHandlerResponse { Url = value, Body = number.Value });
}
[HttpGet("Testing/AntiforgerySimulator/{value}")]
public IActionResult AntiforgerySimulator([FromRoute]int value)
{
Response.Cookies.Append(
"AntiforgerySimulator",
$"Cookie-{value.ToString(CultureInfo.InvariantCulture)}");
return Ok();
}
[HttpPost("Testing/PostRedirectGet/Post/{value}")]
public IActionResult PostRedirectGetPost([FromRoute]int value)
{
var compareValue = $"Cookie-{value.ToString(CultureInfo.InvariantCulture)}";
if (!Request.Cookies.ContainsKey("AntiforgerySimulator"))
{
return BadRequest("Missing AntiforgerySimulator cookie");
}
if (!string.Equals(compareValue, Request.Cookies["AntiforgerySimulator"]))
{
return BadRequest("Values don't match");
}
TempData["Value"] = value + 1;
Response.Cookies.Append("Message", $"Value-{(value + 1).ToString(CultureInfo.InvariantCulture)}");
return RedirectToAction(nameof(PostRedirectGetGet));
}
[HttpGet("Testing/PostRedirectGet/Get/{value}")]
public IActionResult PostRedirectGetGet([FromRoute]int value)
{
return Ok(new PostRedirectGetGetResponse
{
TempDataValue = (int)TempData["Value"],
CookieValue = Request.Cookies["Message"]
});
}
[HttpPut("Testing/Put/{value}")]
public IActionResult PutNoBody([FromRoute]int value)
{
if (value < 5)
{
return RedirectToActionPermanentPreserveMethod(nameof(PutNoBody), "Testing", new { value = value + 1 });
}
else
{
return Ok(value);
}
}
}
public class PostRedirectGetGetResponse
{
public int TempDataValue { get; set; }
public string CookieValue { get; set; }
}
public class RedirectHandlerResponse
{
public int Url { get; set; }
public int Body { get; set; }
}
public class Number
{
public int Value { get; set; }
}
}

View File

@ -65,15 +65,18 @@ namespace ControllersFromServicesWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -79,14 +79,17 @@ namespace CorsWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -24,15 +24,18 @@ namespace ErrorPageMiddlewareWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -26,14 +26,17 @@ namespace FilesWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string [] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -10,14 +10,17 @@ namespace FormatterWebSite
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -44,14 +44,17 @@ namespace HtmlGenerationWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -22,14 +22,17 @@ namespace RazorBuildWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -44,15 +44,18 @@ namespace RazorPageExecutionInstrumentationWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

View File

@ -10,14 +10,17 @@ namespace RazorPagesWebSite
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<StartupWithBasePath>()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<StartupWithBasePath>();
}
}

View File

@ -75,15 +75,18 @@ namespace RazorWebSite
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration()
var host = CreateWebHostBuilder(args)
.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.UseKestrel()
.UseIISIntegration();
}
}

Some files were not shown because too many files have changed in this diff Show More