Testing infrastructure cleanup

This commit is contained in:
Javier Calvarro Nelson 2017-07-06 15:35:06 -07:00
parent f80f7cefa5
commit e1c1682065
11 changed files with 179 additions and 134 deletions

View File

@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc\Microsoft.AspNetCore.Mvc.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Mvc.Core\Microsoft.AspNetCore.Mvc.Core.csproj" />
</ItemGroup>
<ItemGroup>

View File

@ -3,18 +3,15 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Testing.Internal;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.AspNetCore.Mvc.Testing
{
@ -24,8 +21,6 @@ namespace Microsoft.AspNetCore.Mvc.Testing
/// <typeparam name="TStartup">The application startup class.</typeparam>
public class MvcWebApplicationBuilder<TStartup> where TStartup : class
{
private TestCulture _systemCulture;
public string ContentRoot { get; set; }
public IList<Action<IServiceCollection>> ConfigureServicesBeforeStartup { get; set; } = new List<Action<IServiceCollection>>();
public IList<Action<IServiceCollection>> ConfigureServicesAfterStartup { get; set; } = new List<Action<IServiceCollection>>();
@ -82,64 +77,6 @@ namespace Microsoft.AspNetCore.Mvc.Testing
return this;
}
/// <summary>
/// Sets up an <see cref="IStartupFilter"/> that configures the <see cref="CultureReplacerMiddleware"/> at the
/// beginning of the pipeline to change the <see cref="CultureInfo.CurrentCulture"/> and <see cref="CultureInfo.CurrentUICulture"/>
/// of the thread so that they match the cultures in <paramref name="culture"/> and <paramref name="uiCulture"/> for the rest of the
/// <see cref="HttpRequest"/>.
/// </summary>
/// <param name="culture">The culture to use when processing <see cref="HttpRequest"/>.</param>
/// <param name="uiCulture">The UI culture to use when processing <see cref="HttpRequest"/>.</param>
/// <returns>An instance of this <see cref="MvcWebApplicationBuilder{TStartup}"/></returns>
public MvcWebApplicationBuilder<TStartup> UseRequestCulture(string culture, string uiCulture)
{
if (culture == null)
{
throw new ArgumentNullException(nameof(culture));
}
if (uiCulture == null)
{
throw new ArgumentNullException(nameof(uiCulture));
}
ConfigureBeforeStartup(services =>
{
services.TryAddSingleton(new TestCulture
{
Culture = culture,
UICulture = uiCulture
});
services.TryAddSingleton<IStartupFilter, CultureReplacerStartupFilter>();
});
return this;
}
/// <summary>
/// Overrides the <see cref="CultureInfo.CurrentCulture"/> and the <see cref="CultureInfo.CurrentUICulture"/>
/// of the system during the initial configuration of the <see cref="TestHost"/> in case there is any middleware
/// that captures the current culture of the system when the pipeline is being constructed.
/// </summary>
/// <param name="culture">The culture to use when processing <see cref="HttpRequest"/>.</param>
/// <param name="uiCulture">The UI culture to use when processing <see cref="HttpRequest"/>.</param>
/// <returns>An instance of this <see cref="MvcWebApplicationBuilder{TStartup}"/></returns>
public MvcWebApplicationBuilder<TStartup> UseStartupCulture(string culture, string uiCulture)
{
if (culture == null)
{
throw new ArgumentNullException(nameof(culture));
}
if (uiCulture == null)
{
throw new ArgumentNullException(nameof(uiCulture));
}
_systemCulture = new TestCulture { Culture = culture, UICulture = uiCulture };
return this;
}
/// <summary>
/// Configures the application content root.
/// </summary>
@ -185,24 +122,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
.UseContentRoot(ContentRoot)
.ConfigureServices(InitializeServices);
if (_systemCulture == null)
{
return new TestServer(builder);
}
var originalCulture = CultureInfo.CurrentCulture;
var originalUICulture = CultureInfo.CurrentUICulture;
try
{
CultureInfo.CurrentCulture = new CultureInfo(_systemCulture.Culture);
CultureInfo.CurrentUICulture = new CultureInfo(_systemCulture.UICulture);
return new TestServer(builder);
}
finally
{
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUICulture;
}
return new TestServer(builder);
}
protected virtual void InitializeServices(IServiceCollection services)

View File

@ -4,12 +4,7 @@
using System;
using System.IO;
using System.Net.Http;
using System.Reflection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing.Internal;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.AspNetCore.Mvc.Testing
{
@ -21,43 +16,102 @@ namespace Microsoft.AspNetCore.Mvc.Testing
{
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> src/{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("src")
: this(Path.Combine("src", 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 tht 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)
{
var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
// This step assumes project name = assembly name.
var projectName = startupAssembly.GetName().Name;
var projectPath = Path.Combine(solutionRelativePath, projectName);
var builder = new MvcWebApplicationBuilder<TStartup>()
.UseSolutionRelativeContentRoot(projectPath)
.UseApplicationAssemblies()
.UseRequestCulture("en-GB", "en-US")
.UseStartupCulture("en-GB", "en-US");
.UseSolutionRelativeContentRoot(solutionRelativePath)
.UseApplicationAssemblies();
ConfigureApplication(builder);
_server = builder.Build();
_server = CreateServer(builder);
Client = _server.CreateClient();
Client.BaseAddress = new Uri("http://localhost");
}
/// <summary>
/// Creates the <see cref="TestServer"/> with the bootstrapped application in <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="MvcWebApplicationBuilder{TStartup}"/> used to
/// create the server.</param>
/// <returns>The <see cref="TestServer"/> with the bootstrapped application.</returns>
protected virtual TestServer CreateServer(MvcWebApplicationBuilder<TStartup> builder)
{
return builder.Build();
}
/// <summary>
/// Gives a fixture an opportunity to configure the application before it gets built.
/// </summary>
/// <param name="builder">The <see cref="MvcWebApplicationBuilder{TStartup}"/> for the application.</param>
protected virtual void ConfigureApplication(MvcWebApplicationBuilder<TStartup> builder)
{
builder.ConfigureAfterStartup(s => s.TryAddEnumerable(ServiceDescriptor.Transient<IStartupFilter, CultureReplacerStartupFilter>()));
}
/// <summary>
@ -98,7 +152,7 @@ namespace Microsoft.AspNetCore.Mvc.Testing
else
{
for (var i = handlers.Length - 1; i > 1; i++)
for (var i = handlers.Length - 1; i > 1; i--)
{
handlers[i - 1].InnerHandler = handlers[i];
}

View File

@ -5,7 +5,7 @@ using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc.Testing.Internal
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
/// <summary>
/// A middleware that ensures web sites run in a consistent culture. Currently useful for tests that format dates,

View File

@ -5,7 +5,7 @@ using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.Mvc.Testing.Internal
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
/// <summary>
/// Inserts the <see cref="CultureReplacerMiddleware"/> at the beginning of the pipeline.

View File

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

View File

@ -0,0 +1,49 @@
// 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.Globalization;
using System.IO;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class MvcTestFixture<TStartup> : WebApplicationTestFixture<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 ConfigureApplication(MvcWebApplicationBuilder<TStartup> builder)
{
builder.UseRequestCulture("en-GB", "en-US");
builder.ApplicationAssemblies.Clear();
builder.ApplicationAssemblies.Add(typeof(TStartup).GetTypeInfo().Assembly);
}
protected override TestServer CreateServer(MvcWebApplicationBuilder<TStartup> builder)
{
var originalCulture = CultureInfo.CurrentCulture;
var originalUICulture = CultureInfo.CurrentUICulture;
try
{
CultureInfo.CurrentCulture = new CultureInfo("en-GB");
CultureInfo.CurrentUICulture = new CultureInfo("en-US");
return base.CreateServer(builder);
}
finally
{
CultureInfo.CurrentCulture = originalCulture;
CultureInfo.CurrentUICulture = originalUICulture;
}
}
}
}

View File

@ -0,0 +1,49 @@
// 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 Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public static class MvcWebApplicationBuilderExtensions
{
/// <summary>
/// Sets up an <see cref="IStartupFilter"/> that configures the <see cref="CultureReplacerMiddleware"/> at the
/// beginning of the pipeline to change the <see cref="CultureInfo.CurrentCulture"/> and <see cref="CultureInfo.CurrentUICulture"/>
/// of the thread so that they match the cultures in <paramref name="culture"/> and <paramref name="uiCulture"/> for the rest of the
/// <see cref="HttpRequest"/>.
/// </summary>
/// <param name="culture">The culture to use when processing <see cref="HttpRequest"/>.</param>
/// <param name="uiCulture">The UI culture to use when processing <see cref="HttpRequest"/>.</param>
/// <returns>An instance of this <see cref="MvcWebApplicationBuilder{TStartup}"/></returns>
public static MvcWebApplicationBuilder<TStartup> UseRequestCulture<TStartup>(this MvcWebApplicationBuilder<TStartup> builder, string culture, string uiCulture)
where TStartup : class
{
if (culture == null)
{
throw new ArgumentNullException(nameof(culture));
}
if (uiCulture == null)
{
throw new ArgumentNullException(nameof(uiCulture));
}
builder.ConfigureBeforeStartup(services =>
{
services.TryAddSingleton(new TestCulture
{
Culture = culture,
UICulture = uiCulture
});
services.TryAddEnumerable(ServiceDescriptor.Singleton<IStartupFilter, CultureReplacerStartupFilter>());
});
return builder;
}
}
}

View File

@ -1,7 +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.
namespace Microsoft.AspNetCore.Mvc.Testing.Internal
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class TestCulture
{

View File

@ -1,30 +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;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Testing;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class MvcTestFixture<TStartup> : WebApplicationTestFixture<TStartup>
where TStartup : class
{
public MvcTestFixture()
: base(Path.Combine("test", "WebSites"))
{
}
protected MvcTestFixture(string solutionRelativePath)
: base(solutionRelativePath)
{
}
protected override void ConfigureApplication(MvcWebApplicationBuilder<TStartup> builder)
{
base.ConfigureApplication(builder);
builder.ApplicationAssemblies.Clear();
builder.ApplicationAssemblies.Add(typeof(TStartup).GetTypeInfo().Assembly);
}
}
}