2016-11-04 124 views
0

單元測試考慮代碼:的競爭條件

class TestClass 
{ 
    private bool _someFlag; 
    private object _sharedObject = new object(); 
    private readonly object _syncObject = new object(); 

    public object Read() 
    { 
     //lock (_syncObject) 
     { 
      _someFlag = false; 
      return _sharedObject; 
     } 
    } 

    public void Write(object obj) 
    { 
     //lock (_syncObject) 
     { 
      _someFlag = true; 
      _sharedObject = obj; 
     } 
    } 
} 

它有競爭狀態的問題。當我們撥打Read()某些線程可以撥打_someFlag = false;return _sharedObject;行之間的Write()行。我要通過lock運營商修復問題。但是請你幫我單元測試一下這個競賽狀態問題。

我不想將_someFlag更改爲public用於測試目的或類似的東西。 我想要做這樣的事情:

[Fact] 
public void RaceConditionTest() 
{ 
    var correctObject = new object(); 
    var test = new TestClass(); 

    for (int i = 0; i < 1000; i++) 
    { 
     test.Write(correctObject); 
     var assertTask = Task.Run(() => 
     { 
      var actualObj = test.Read(); 
      Assert.True(object.ReferenceEquals(correctObject, actualObj), $"Failed on {i} iteration"); 
     }); 
     //Thread.Sleep(50); 
     var failTask = Task.Run(() => test.Write(new object())); 

     Task.WaitAll(assertTask, failTask); 
    } 
} 

但我怎麼能確定assertTaskfailTask之前啓動?或者,也許有另一種方法來單元測試這種情況?提前致謝。

+0

對於單元測試,競爭條件可能是不可能的,無論是正面的,還是*尤其是負面的。爲了讓代碼可靠地做你不想要的東西,你必須特別加入同步機制,強制操作以可靠的順序進行,以便你可以可靠地重現不良行爲。但是,要在同步中添加可靠地產生不良行爲,您現在*強制*代碼行爲錯誤;你可以很容易地寫出它的行爲是否正確,如果你不確定是否有,你不能依賴測試。 – Servy

回答

0

我住這種方法。但仍然在尋找更好的方法... 此測試在某些迭代中失敗,但如果您取消註釋lock運算符,測試將通過。

class TestClass 
{ 
    private IEventRecorder _eventRecorder; 


    private bool _someFlag; 
    private object _sharedObject = new object(); 
    private readonly object _syncObject = new object(); 

#if DEBUG 
    public void SetEventRecorder(IEventRecorder eventRecorder) => _eventRecorder = eventRecorder; 
#endif 

    public object Read() 
    { 
     //lock (_syncObject) 
     { 
#if DEBUG 
      _eventRecorder?.Record(nameof(Read)); 
#endif 
      _someFlag = false; 
      return _sharedObject; 
     } 
    } 

    public void Write(object obj) 
    { 
     //lock (_syncObject) 
     { 
#if DEBUG 
      _eventRecorder?.Record(nameof(Write)); 
#endif 
      _someFlag = true; 
      _sharedObject = obj; 
     } 
    } 

    public interface IEventRecorder 
    { 
     void Record(string eventName); 
    } 
} 

public class TestClassTests 
{ 
    private class EventRecorder : TestClass.IEventRecorder 
    { 
     private string _events = string.Empty; 

     public void Record(string eventName) => _events += eventName; 

     public string Events => _events; 

     public void Reset() => _events = string.Empty; 
    } 

    [Fact] 
    public void RaceConditionTest() 
    { 
     var correctObject = new object(); 
     var eventRecorder = new EventRecorder(); 
     var test = new TestClass(); 
     test.SetEventRecorder(eventRecorder); 

     for (int i = 0; i < 1000; i++) 
     { 
      test.Write(correctObject); 
      var assertTask = Task.Run(() => 
      { 
       var actualObj = test.Read(); 
       if (eventRecorder.Events.StartsWith("WriteRead")) 
        Assert.True(object.ReferenceEquals(correctObject, actualObj), $"Failed on {i} iteration"); 
      }); 
      var failTask = Task.Run(() => test.Write(new object())); 

      Task.WaitAll(assertTask, failTask); 
      eventRecorder.Reset(); 
     } 
    } 
} 
0

你可以,如果assertTask正在運行或啓動failTask​​前完成檢查:

while (assertTask.Status != Running && assertTask.Status != RanToCompletion) 
Thread.Sleep(50); 
+0

這如何幫助測試競賽狀態?我需要同時執行這兩個任務。如果我有斷言任務完成,我無法重現競賽狀態。 – Serg046

+0

答案是「我怎樣才能確保** assertTask將在failTask​​之前啓動**」就像我上面提到的那樣檢查任務狀態。沒有保證重現競賽狀況的方法。您只能通過多次運行此循環來重現,超過1000次 –