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(), некоторые темы могут звонить Write() между _someFlag = false; и return _sharedObject; строк. Я собираюсь исправить проблему оператором 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); 
    } 
} 

Но как я могу быть уверен, что assertTask будет запущен до failTask? Или, может быть, есть еще один способ проверить этот случай? Заранее спасибо.

+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

Как это помогает проверить состояние гонки? Мне нужно, чтобы обе задачи выполнялись одновременно. Если у меня есть assertTask завершено, я не могу воспроизвести состояние гонки. – Serg046

+0

ответ на вопрос «как я могу быть уверен **, что assertTask будет запущен ** до того, как failTask» будет проверять состояние задачи, как я предложил выше. Нет никакого гарантированного способа воспроизвести состояние гонки. Вы можете воспроизводить только этот цикл много раз, более 1000 раз –