Как я могу обеспечить покрытие многопоточного кода - PullRequest
0 голосов
/ 27 ноября 2018

Я пытаюсь написать модульный тест, который может охватить кусок многопоточного кода 100% времени для покрытия кода.Код можно получить ТОЛЬКО в контексте многопоточного приложения, и он спроектирован так, чтобы его можно было запускать только один раз для каждого экземпляра объекта, чтобы минимизировать затраты времени блокировки.

В качестве упрощенного примера:

public class Example
{
  private readonly object lockObject = new object();
  private int? objectToInit = null;

  public void EnsureObjectInit()
  {
    //To minimize hitting the lock code, the object is checked first.
    if (!objectToInit.HasValue)
    {
      lock (lockObject)
      {
        //Then it is checked once more to ensure that it wasn't initiazlized
        //before the lock was hit.
        if (objectToInit.HasValue)
        {
          //This block can only be executed if a second thread is able to
          //get to the lock before the first can initialize the object.

          return; 
        }

        objectToInit = 0;
      }
    }
  }
}

Чтобы получить покрытие кода для попадания кода во второй оператор if, я пробовал код, подобный следующему:

[TestClass]
public class ExampleTest
{
  [Test Method]
  public void async MyTest()
  {
    Example example = new Example();

    void runTask()
    {
      example.EnsureObjectInit();
    }

    //I've tried increasing the number of threads, but it doesn't make
    //it hit the noted code any more consistently.
    List<Task> tasks = new List<Task>(2);
    tasks.Add(Task.Run(runTask));
    tasks.Add(Task.Run(runTask));

    await Task.WhenAll(tasks);
    ... perform extra validation ...
  }
}

Но покрытие кода не достигает внутреннего блока if, по крайней мере, на 50%время.

Есть ли способ заставить тестируемый модулем код остановить первый поток перед инициализацией объекта, чтобы второй поток мог попасть в первый блок if?

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

С небольшим изменением дизайна вы можете упростить тестирование вашего кода: Извлеките доступ к objectToInit.HasValue в вспомогательный метод, такой как hasObjectToInitValue().В своем тесте вы можете затем переопределить этот метод и затем протестировать сценарий, в котором вспомогательный метод при первом вызове возвращает false, а при втором возвращает true.

0 голосов
/ 27 ноября 2018

Это немного хак, но вы можете использовать отражение, чтобы получить lockObject из экземпляра класса Example.Затем заблокируйте его вручную в методе ExampleTest.ПРИМЕЧАНИЕ: это очень сложный тест.Если вы измените имя замка, тест не пройден.

    private class LockExample
    {
        private readonly object lockObject = new object();

        public void TestLock()
        {
            lock(lockObject)
            {
                //Do something
            }
        }
    }

    private class LockTest
    {
        public void Test()
        {
            var example = new LockExample();
            var lockOjbect = typeof(LockExample).GetField("lockObject", BindingFlags.NonPublic|BindingFlags.Instance).GetValue(example);
            lock (lockOjbect)
            {
                var task = Task.Run((Action)example.TestLock);
                task.Wait(1);   //allow the other thread to run
            }
        }
    }
...