理论上按顺序(而不是并行)执行Xunit测试



我们在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

遗憾的是,您只能对类应用此属性。

所以,你需要一个小的重构,像这样:
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/");
}
}