[英]How can I log the execution order of tests in xUnit
I have a large test set (5k+) using xUnit.net, and I'm having concurrency problems among tests running in parallel. 我有一个使用xUnit.net的大型测试集(5k +),并且我在并行运行的测试中遇到并发问题。
xUnit randomizes the execution order of tests, which makes it harder for me to detect the issue. xUnit使测试的执行顺序随机化,这使我更难以检测到问题。
I'd like to know whether is there a way to log, during test execution, the moment a test starts and the moment it ends. 我想知道是否有一种方法可以在测试执行期间记录测试开始的时刻和结束的时刻。
Note: Using constructor and disposer methods does not cut it, because you cannot know which test is being run on the constructor/disposer. 注意:使用构造函数和disposer方法不会削减它,因为您无法知道构造函数/ disposer上正在运行哪个测试。
Note 2: In case it is not obvious, I'm looking for a solution that doesn't involve writing log call in each test. 注2:如果不明显,我正在寻找一种不涉及在每次测试中编写日志调用的解决方案。
Thanks, 谢谢,
Well, I managed to do it using the BeforeAfterTestAttribute
from xUnit. 好吧,我设法使用xUnit中的
BeforeAfterTestAttribute
来完成它。 Then I wrote the utility logger below to output the results to a .csv
file. 然后我编写了下面的实用程序记录器,将结果输出到
.csv
文件。
public class LogTestExecutionAttribute: BeforeAfterTestAttribute
{
public override void Before(MethodInfo methodUnderTest)
{
TestExecutionDataLogger.LogBegin(methodUnderTest);
}
public override void After(MethodInfo methodUnderTest)
{
TestExecutionDataLogger.LogEnd(methodUnderTest);
}
}
public static class TestExecutionDataLogger
{
private static readonly string LogFileName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "DbCoud", $"UnitTests_{DateTime.UtcNow:yyyy_MM_dd_HH_mm}_D_{AppDomain.CurrentDomain.Id}.csv");
private static int _startedOrder = 0;
private static int _endedOrder = 0;
private static readonly ConcurrentDictionary<string, testExecutionData> testDataDict = new ConcurrentDictionary<string, testExecutionData>();
private static readonly ConcurrentQueue<string> logQueue = new ConcurrentQueue<string>();
public static void LogBegin(MethodInfo testInfo)
{
var name = $"{testInfo.DeclaringType.FullName}.{testInfo.Name}";
var order = Interlocked.Add(ref _startedOrder, 1);
var startedUtc = DateTime.UtcNow;
var data = testDataDict.GetOrAdd(name, new testExecutionData());
data.StartedUtc = startedUtc;
data.StartedOrder = order;
data.TestName = name;
data.Status = "Started";
data.StartThreadId = Thread.CurrentThread.ManagedThreadId;
writeLog(data);
}
public static void LogEnd(MethodInfo testInfo)
{
var name = $"{testInfo.DeclaringType.FullName}.{testInfo.Name}";
var dataEndedUtc = DateTime.UtcNow;
var order = Interlocked.Add(ref _endedOrder, 1);
var data = testDataDict[name];
data.EndedUtc = dataEndedUtc;
data.EndedOrder = order;
data.Status = "Ended";
data.EndThreadId = Thread.CurrentThread.ManagedThreadId;
writeLog(data);
}
private static void writeLog(testExecutionData data)
{
logQueue.Enqueue(data.ToCsvLine());
if (data.EndedOrder == 1)
{
Directory.CreateDirectory(Path.GetDirectoryName(LogFileName));
Task.Run(logWriter);
}
}
private static Task logWriter()
{
while (true)
{
var logs = new List<string>();
string result;
while (logQueue.TryDequeue(out result))
{
logs.Add(result);
}
if (logs.Any())
{
File.AppendAllLines(LogFileName, logs);
}
}
}
private class testExecutionData
{
public int StartedOrder { get; set; }
public int EndedOrder { get; set; }
public DateTime StartedUtc { get; set; }
public DateTime EndedUtc { get; set; }
public string TestName { get; set; }
public string Status { get; set; }
public int StartThreadId { get; set; }
public int EndThreadId { get; set; }
public string ToCsvLine() { return $"{TestName};{Status};{StartedOrder};{EndedOrder};{StartedUtc:o};{EndedUtc:o};{Math.Max(0, ( EndedUtc - StartedUtc ).TotalMilliseconds)};{StartThreadId};{EndThreadId}"; }
}
}
To use this code, add the LogTestExecutionAttribute
to the test classes you want to log (or to the base classes ;p). 要使用此代码,请将
LogTestExecutionAttribute
添加到要记录的测试类(或基类; p)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.