2016-11-16 30 views
1

我有一个使用xUnit.net的大型测试集(5k +),并且在并行运行的测试中遇到并发问题。如何在xUnit中记录测试的执行顺序

xUnit随机化测试的执行顺序,这使我很难检测到问题。

我想知道在测试执行期间是否有方法记录测试开始和结束的时刻。

注意:使用构造函数和disposer方法不会削减它,因为您无法知道哪个测试正在构造函数/处理器上运行。注2:如果不明显,我正在寻找不涉及在每个测试中写日志调用的解决方案。

感谢,

+0

而不是记录 - 让你的测试彼此独立。你使用什么样的测试跑步者?如果没有记错,Visual Studio将设置为“并行测试”,将只运行不同线程上不同项目的测试。同一个项目的测试将在同一个线程上执行。 – Fabio

+0

@Fabio,我不知道我的测试在哪里共享数据。我怀疑这是NSubstitute。我正在努力记录,以便能够使他们独立。 xUnit并行运行测试。 – LMB

+0

来自xUnit文档:_相同测试类中的测试不会并行运行。所以你需要确定测试类没有共享状态。 – Fabio

回答

2

好吧,我设法利用的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)。

相关问题