如何在集成测试项目中替换中间件
How to replace Middleware in integration tests project
我有启动 cs,我在其中注册 AuthenticationMiddleware,如下所示:
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
AddAuthentication(app);
app.UseMvcWithDefaultRoute();
app.UseStaticFiles();
}
protected virtual void AddAuthentication(IApplicationBuilder app)
{
app.UseAuthentication();
}
}
我使用以下方法对其进行测试:
WebApplicationFactory<Startup>().CreateClient();
问题:
我想用app.UseMiddleware<TestingAuthenticationMiddleware>()
、
替换app.UseAuthentication();
我尝试过的:
我想过在我的测试项目中从 Startup 继承:
public class TestStartup : Startup
{
protected override void AddAuthentication(IApplicationBuilder app)
{
app.UseMiddleware<AuthenticatedTestRequestMiddleware>();
}
}
class TestWebApplicationFactory : WebApplicationFactory<Web.Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder()
.UseStartup<IntegrationTestProject.TestStartup>();
}
}
但这不起作用,因为 TestStartup 在另一个程序集中,这对 WebHost.CreateDefaultBuilder()
有很多副作用
我得到:
System.ArgumentException: The content root
'C:\Projects\Liero\myproject\tests\IntegrationTests' does not exist.
Parameter name: contentRootPath
我遇到了同样的问题,这样解决了;
/// <summary>
/// A test fixture which hosts the target project (project we wish to test) in an in-memory server.
/// </summary>
/// <typeparam name="TStartup">Target project's startup type</typeparam>
/// <typeparam name="DStartup">Decorated startup type</typeparam>
public class TestFixture<DStartup, TStartup> : IDisposable
{
private readonly TestServer _server;
public TestFixture()
: this(Path.Combine("YourRelativeTargetProjectParentDir"))
{
}
protected TestFixture(string relativeTargetProjectParentDir)
{
var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
var contentRoot = GetProjectPath(relativeTargetProjectParentDir, startupAssembly);
//var integrationTestsPath = PlatformServices.Default.Application.ApplicationBasePath;
//var contentRoot = Path.GetFullPath(Path.Combine(integrationTestsPath, "../../../../MinasTirith"));
var builder = new WebHostBuilder()
.UseContentRoot(contentRoot)
.ConfigureServices(InitializeServices)
.UseEnvironment("Development")
.UseStartup(typeof(DStartup));
_server = new TestServer(builder);
Client = _server.CreateClient();
Client.BaseAddress = new Uri("http://localhost:5000");
}
public HttpClient Client { get; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
Client.Dispose();
_server.Dispose();
}
protected virtual void InitializeServices(IServiceCollection services)
{
var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
// Inject a custom application part manager.
// Overrides AddMvcCore() because it uses TryAdd().
var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));
manager.FeatureProviders.Add(new ControllerFeatureProvider());
manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
services.AddSingleton(manager);
}
/// <summary>
/// Gets the full path to the target project that we wish to test
/// </summary>
/// <param name="projectRelativePath">
/// The parent directory of the target project.
/// e.g. src, samples, test, or test/Websites
/// </param>
/// <param name="startupAssembly">The target project's assembly.</param>
/// <returns>The full path to the target project.</returns>
private static string GetProjectPath(string projectRelativePath, Assembly startupAssembly)
{
// Get name of the target project which we want to test
var projectName = startupAssembly.GetName().Name;
// Get currently executing test project path
var applicationBasePath = System.AppContext.BaseDirectory;
// Find the path to the target project
var directoryInfo = new DirectoryInfo(applicationBasePath);
do
{
directoryInfo = directoryInfo.Parent;
var projectDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, projectRelativePath));
if (projectDirectoryInfo.Exists)
{
var projectFileInfo = new FileInfo(Path.Combine(projectDirectoryInfo.FullName, projectName, $"{projectName}.csproj"));
if (projectFileInfo.Exists)
{
return Path.Combine(projectDirectoryInfo.FullName, projectName);
}
}
}
while (directoryInfo.Parent != null);
throw new Exception($"Project root could not be located using the application root {applicationBasePath}.");
}
}
我在我的单元测试方法中使用它,就像这样;
[TestInitialize]
public void BeforeEachTest()
{
testFixture = new TestFixture<TestStartup, Startup>();
//this is my HttpClient variable
client = testFixture.Client;
}
P.S。这是我在我的项目中使用的确切代码片段。
WebApplicationFactory 似乎应该使用真正的 Startup class 作为类型参数:
class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder<TestableStartup>(new string[0]);
}
}
我有启动 cs,我在其中注册 AuthenticationMiddleware,如下所示:
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
AddAuthentication(app);
app.UseMvcWithDefaultRoute();
app.UseStaticFiles();
}
protected virtual void AddAuthentication(IApplicationBuilder app)
{
app.UseAuthentication();
}
}
我使用以下方法对其进行测试:
WebApplicationFactory<Startup>().CreateClient();
问题:
我想用app.UseMiddleware<TestingAuthenticationMiddleware>()
、
app.UseAuthentication();
我尝试过的:
我想过在我的测试项目中从 Startup 继承:
public class TestStartup : Startup
{
protected override void AddAuthentication(IApplicationBuilder app)
{
app.UseMiddleware<AuthenticatedTestRequestMiddleware>();
}
}
class TestWebApplicationFactory : WebApplicationFactory<Web.Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder()
.UseStartup<IntegrationTestProject.TestStartup>();
}
}
但这不起作用,因为 TestStartup 在另一个程序集中,这对 WebHost.CreateDefaultBuilder()
我得到:
System.ArgumentException: The content root 'C:\Projects\Liero\myproject\tests\IntegrationTests' does not exist. Parameter name: contentRootPath
我遇到了同样的问题,这样解决了;
/// <summary>
/// A test fixture which hosts the target project (project we wish to test) in an in-memory server.
/// </summary>
/// <typeparam name="TStartup">Target project's startup type</typeparam>
/// <typeparam name="DStartup">Decorated startup type</typeparam>
public class TestFixture<DStartup, TStartup> : IDisposable
{
private readonly TestServer _server;
public TestFixture()
: this(Path.Combine("YourRelativeTargetProjectParentDir"))
{
}
protected TestFixture(string relativeTargetProjectParentDir)
{
var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
var contentRoot = GetProjectPath(relativeTargetProjectParentDir, startupAssembly);
//var integrationTestsPath = PlatformServices.Default.Application.ApplicationBasePath;
//var contentRoot = Path.GetFullPath(Path.Combine(integrationTestsPath, "../../../../MinasTirith"));
var builder = new WebHostBuilder()
.UseContentRoot(contentRoot)
.ConfigureServices(InitializeServices)
.UseEnvironment("Development")
.UseStartup(typeof(DStartup));
_server = new TestServer(builder);
Client = _server.CreateClient();
Client.BaseAddress = new Uri("http://localhost:5000");
}
public HttpClient Client { get; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
Client.Dispose();
_server.Dispose();
}
protected virtual void InitializeServices(IServiceCollection services)
{
var startupAssembly = typeof(TStartup).GetTypeInfo().Assembly;
// Inject a custom application part manager.
// Overrides AddMvcCore() because it uses TryAdd().
var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new AssemblyPart(startupAssembly));
manager.FeatureProviders.Add(new ControllerFeatureProvider());
manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
services.AddSingleton(manager);
}
/// <summary>
/// Gets the full path to the target project that we wish to test
/// </summary>
/// <param name="projectRelativePath">
/// The parent directory of the target project.
/// e.g. src, samples, test, or test/Websites
/// </param>
/// <param name="startupAssembly">The target project's assembly.</param>
/// <returns>The full path to the target project.</returns>
private static string GetProjectPath(string projectRelativePath, Assembly startupAssembly)
{
// Get name of the target project which we want to test
var projectName = startupAssembly.GetName().Name;
// Get currently executing test project path
var applicationBasePath = System.AppContext.BaseDirectory;
// Find the path to the target project
var directoryInfo = new DirectoryInfo(applicationBasePath);
do
{
directoryInfo = directoryInfo.Parent;
var projectDirectoryInfo = new DirectoryInfo(Path.Combine(directoryInfo.FullName, projectRelativePath));
if (projectDirectoryInfo.Exists)
{
var projectFileInfo = new FileInfo(Path.Combine(projectDirectoryInfo.FullName, projectName, $"{projectName}.csproj"));
if (projectFileInfo.Exists)
{
return Path.Combine(projectDirectoryInfo.FullName, projectName);
}
}
}
while (directoryInfo.Parent != null);
throw new Exception($"Project root could not be located using the application root {applicationBasePath}.");
}
}
我在我的单元测试方法中使用它,就像这样;
[TestInitialize]
public void BeforeEachTest()
{
testFixture = new TestFixture<TestStartup, Startup>();
//this is my HttpClient variable
client = testFixture.Client;
}
P.S。这是我在我的项目中使用的确切代码片段。
WebApplicationFactory 似乎应该使用真正的 Startup class 作为类型参数:
class TestWebApplicationFactory : WebApplicationFactory<Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost.CreateDefaultBuilder<TestableStartup>(new string[0]);
}
}