我有一个使用xUnit.net的大型测试集(5k +),并且我在并行运行的测试中遇到并发问题。
xUnit使测试的执行顺序随机化,这使我更难以发现问题。
我想知道在测试执行期间是否有办法记录测试开始的时刻和结束的时刻。
注意:使用构造函数和disposer方法不会削减它,因为您无法知道构造函数/ disposer上正在运行哪个测试。
注2:如果不明显,我正在寻找一种不涉及在每次测试中编写日志调用的解决方案。
谢谢,
答案 0 :(得分:3)
好吧,我设法使用xUnit中的BeforeAfterTestAttribute
。然后我编写了下面的实用程序记录器,将结果输出到.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}"; }
}
}
要使用此代码,请将LogTestExecutionAttribute
添加到要记录的测试类(或基类; p)。