理论上按顺序执行 Xunit 测试(非并行)
Execute Xunit tests in theory sequentially (not parallel)
我们在 git 存储库中托管不同环境的配置文件。作为 CI 过程的一部分,我想确保这些配置文件始终有效。为此,我创建了这个测试,它复制配置,尝试启动服务器并立即关闭它。
public class DeployConfigurationValidationTests
{
#region Private Fields
private readonly ITestOutputHelper _testOutputHelper;
private const string ServerBaseUrl = "http://localhost:44315";
#endregion
#region Constructors
public DeployConfigurationValidationTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
#endregion
#region Public Tests
/// <summary>
/// Copies all files contained in the directory specified by parameter <see cref="deployConfigDirectoryPath"/> to the executing directory and launches the application with this configuration.
/// </summary>
/// <param name="deployConfigDirectoryPath">The path of the directory containing the deploy configurations</param>
[Theory]
[InlineData("../../../../../Configurations/Dev/")]
[InlineData("../../../../../Configurations/Int/")]
[InlineData("../../../../../Configurations/Prod/")]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
// Arrange (copy deploy configurations into directory where the test is running)
var currentDirectory = Directory.GetCurrentDirectory();
var configurationFilePaths = Directory.GetFiles(deployConfigDirectoryPath);
foreach (var configurationFilePath in configurationFilePaths)
{
var configurationFileName = Path.GetFileName(configurationFilePath);
var destinationFilePath = Path.Combine(currentDirectory, configurationFileName);
File.Copy(configurationFilePath, Path.Combine(currentDirectory, destinationFilePath), true);
_testOutputHelper.WriteLine($"Copied file '{Path.GetFullPath(configurationFilePath)}' to '{destinationFilePath}'");
}
// Act (launch the application with the deploy config)
var hostBuilder = Program.CreateHostBuilder(null)
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.UseUrls(ServerBaseUrl);
webHostBuilder.UseTestServer();
});
using var host = await hostBuilder.StartAsync();
// Assert
// Nothing to assert, if no error occurs, the config is fine
}
#endregion
}
测试在 运行 单独 InlineData
时工作正常,但在 运行 理论时失败,因为默认情况下测试是 运行 并行的。使用相同的 DLL 在同一个端口上启动多个(测试)服务器显然是行不通的。
问题:如何告诉 xUnit 按顺序 运行 这些测试?
我们使用 .net core 3.1 和 XUnit 2.4.1
解决此问题的一种方法是使用 CollectionAttribute
.
很遗憾,您只能将此属性应用于 classes。
因此,您需要像这样进行小型重构:
internal class ValidateDeploymentConfigBase
{
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
// Arrange
var currentDirectory = Directory.GetCurrentDirectory();
var configurationFilePaths = Directory.GetFiles(deployConfigDirectoryPath);
foreach (var configurationFilePath in configurationFilePaths)
{
var configurationFileName = Path.GetFileName(configurationFilePath);
var destinationFilePath = Path.Combine(currentDirectory, configurationFileName);
File.Copy(configurationFilePath, Path.Combine(currentDirectory, destinationFilePath), true);
_testOutputHelper.WriteLine($"Copied file '{Path.GetFullPath(configurationFilePath)}' to '{destinationFilePath}'");
}
var hostBuilder = Program.CreateHostBuilder(null)
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.UseUrls(ServerBaseUrl);
webHostBuilder.UseTestServer();
});
// Act
using var host = await hostBuilder.StartAsync();
}
}
然后您的测试用例将如下所示:
[Collection("Sequential")]
internal class ValidateDevDeploymentConfig: ValidateDeploymentConfigBase
{
[Fact]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
base.ValidateDeployConfigurationsTest("../../../../../Configurations/Dev/");
}
}
...
[Collection("Sequential")]
internal class ValidateProdDeploymentConfig : ValidateDeploymentConfigBase
{
[Fact]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
base.ValidateDeployConfigurationsTest("../../../../../Configurations/Prod/");
}
}
我们在 git 存储库中托管不同环境的配置文件。作为 CI 过程的一部分,我想确保这些配置文件始终有效。为此,我创建了这个测试,它复制配置,尝试启动服务器并立即关闭它。
public class DeployConfigurationValidationTests
{
#region Private Fields
private readonly ITestOutputHelper _testOutputHelper;
private const string ServerBaseUrl = "http://localhost:44315";
#endregion
#region Constructors
public DeployConfigurationValidationTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
#endregion
#region Public Tests
/// <summary>
/// Copies all files contained in the directory specified by parameter <see cref="deployConfigDirectoryPath"/> to the executing directory and launches the application with this configuration.
/// </summary>
/// <param name="deployConfigDirectoryPath">The path of the directory containing the deploy configurations</param>
[Theory]
[InlineData("../../../../../Configurations/Dev/")]
[InlineData("../../../../../Configurations/Int/")]
[InlineData("../../../../../Configurations/Prod/")]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
// Arrange (copy deploy configurations into directory where the test is running)
var currentDirectory = Directory.GetCurrentDirectory();
var configurationFilePaths = Directory.GetFiles(deployConfigDirectoryPath);
foreach (var configurationFilePath in configurationFilePaths)
{
var configurationFileName = Path.GetFileName(configurationFilePath);
var destinationFilePath = Path.Combine(currentDirectory, configurationFileName);
File.Copy(configurationFilePath, Path.Combine(currentDirectory, destinationFilePath), true);
_testOutputHelper.WriteLine($"Copied file '{Path.GetFullPath(configurationFilePath)}' to '{destinationFilePath}'");
}
// Act (launch the application with the deploy config)
var hostBuilder = Program.CreateHostBuilder(null)
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.UseUrls(ServerBaseUrl);
webHostBuilder.UseTestServer();
});
using var host = await hostBuilder.StartAsync();
// Assert
// Nothing to assert, if no error occurs, the config is fine
}
#endregion
}
测试在 运行 单独 InlineData
时工作正常,但在 运行 理论时失败,因为默认情况下测试是 运行 并行的。使用相同的 DLL 在同一个端口上启动多个(测试)服务器显然是行不通的。
问题:如何告诉 xUnit 按顺序 运行 这些测试?
我们使用 .net core 3.1 和 XUnit 2.4.1
解决此问题的一种方法是使用 CollectionAttribute
.
很遗憾,您只能将此属性应用于 classes。
因此,您需要像这样进行小型重构:
internal class ValidateDeploymentConfigBase
{
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
// Arrange
var currentDirectory = Directory.GetCurrentDirectory();
var configurationFilePaths = Directory.GetFiles(deployConfigDirectoryPath);
foreach (var configurationFilePath in configurationFilePaths)
{
var configurationFileName = Path.GetFileName(configurationFilePath);
var destinationFilePath = Path.Combine(currentDirectory, configurationFileName);
File.Copy(configurationFilePath, Path.Combine(currentDirectory, destinationFilePath), true);
_testOutputHelper.WriteLine($"Copied file '{Path.GetFullPath(configurationFilePath)}' to '{destinationFilePath}'");
}
var hostBuilder = Program.CreateHostBuilder(null)
.ConfigureWebHostDefaults(webHostBuilder =>
{
webHostBuilder.UseUrls(ServerBaseUrl);
webHostBuilder.UseTestServer();
});
// Act
using var host = await hostBuilder.StartAsync();
}
}
然后您的测试用例将如下所示:
[Collection("Sequential")]
internal class ValidateDevDeploymentConfig: ValidateDeploymentConfigBase
{
[Fact]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
base.ValidateDeployConfigurationsTest("../../../../../Configurations/Dev/");
}
}
...
[Collection("Sequential")]
internal class ValidateProdDeploymentConfig : ValidateDeploymentConfigBase
{
[Fact]
public async Task ValidateDeployConfigurationsTest(string deployConfigDirectoryPath)
{
base.ValidateDeployConfigurationsTest("../../../../../Configurations/Prod/");
}
}