2015-06-02 4 views
4

У меня есть метод C#, который будет вызываться несколько раз с использованием разных потоков. Итак, я хочу создать единичный тест, который будет проверять этот метод на несколько потоков, но я не уверен, что делаю это правильно.Тест многопоточного блока

Это мой блок тест без резьбы:

[TestMethod] 
    public void FromLocalPoints() 
    { 
     var projectedCoordinates = this.ConvertFromLocalPoints(); 
     foreach (var projectedCoordinate in projectedCoordinates) 
     { 
      Debug.Write(projectedCoordinate.X); 
      Debug.Write("; "); 
      Debug.WriteLine(projectedCoordinate.Y); 
     } 
    } 

this.ConvertFromLocalPoints() вызывает фактический метод, который я хочу, чтобы проверить.

Я создал делегат, событие и обработчик:

public delegate void ReprojectCompleteHandler(IEnumerable<Coordinate> projectedCoordinates); 
public event ReprojectCompleteHandler ReprojectCompleteEvent; 
private void ReprojectHandler(IEnumerable<Coordinate> projectedCoordinates) 
{ 
     Debug.WriteLine("Conversion is complete"); 
} 

В моей TestSetup я слушаю мое событие:

[TestInitialize] 
    public void TestSetup() 
    { 
     this.ReprojectCompleteEvent += this.ReprojectHandler; 
    } 

Мой блок тест является:

[TestMethod] 
    public void FromLocalPointsThreaded() 
    { 
     // Call FromLocalPoints multiple times in separate threads to check if it is thread safe 
     for (var i = 0; i < 10; i++) 
     { 
      var myThread = new Thread(this.ConvertFromLocalPointsThreaded);  
     } 

     Debug.WriteLine("FromLocalPointsThreaded is done"); 
    } 

    private void ConvertFromLocalPointsThreaded() 
    { 
     var projectedCoordinates = this.ConvertFromLocalPoints(); 

     // Send result to delegate/event: 
     if (this.ReprojectCompleteEvent != null) 
     { 
      this.ReprojectCompleteEvent(projectedCoordinates); 
     } 
    } 

Когда я запускаю этот модульный тест, я получаю сообщение «FromLocalPointsThreaded is done» в моем выпуске, но «Конверсия не завершена».

Что мне не хватает, чтобы получить эту работу? Или я должен использовать другой подход?

Обновление В настоящее время мы переводим библиотеки, которые выполняют фактическое преобразование. Старая библиотека не является потокобезопасной, поэтому мы добавили блокировки. Новая библиотека должна быть потокобезопасной, поэтому я хочу удалить блокировки. Но мне нужен единичный тест, который докажет, что наш метод с использованием новой библиотеки действительно потокобезопасен.

+0

Нет ничего, что говорит основному потоку ждать появления порожденных потоков. Он просто выходит, прежде чем они начнут работать. Посмотрите на Thread.Join. – mrmcgreg

+0

Нет, где в вашем коде вы фактически _start_ нити – MickyD

+0

В дополнение к ответам выше (как mrmcgreg's, так и Micky Duncan's), если вы хотите измерить производительность вашего кода в параллельной среде, вы можете начать свои потоковые функции в один заход. См. [Мой ответ по другому вопросу] (http://stackoverflow.com/a/30568340/2144232) на hpw, это можно сделать на C#. – mg30rg

ответ

0

Как я предложил, я переключил свой подход. Я теперь с помощью:

[TestMethod] 
    public void FromLocalPointsParallel() 
    { 
     var loop = new int[10]; 
     Parallel.ForEach(
      loop, 
      item => 
       { 
        var projectedCoordinates = this.ConvertToLocalPoints(); 
        foreach (var projectedCoordinate in projectedCoordinates) 
        { 
         var x = projectedCoordinate.X; 
         var y = projectedCoordinate.Y; 
         Assert.IsFalse(double.IsInfinity(x) || double.IsNaN(x), "Projected X is not a valid number"); 
         Assert.IsFalse(double.IsInfinity(y) || double.IsNaN(y), "Projected Y is not a valid number"); 
        } 
       }); 
    } 

тест Это устройство делает то, что мне нужно: вызвать мой метод преобразования в различных потоках. И это уже доказало его использование, поскольку я обнаружил, что словарь, который я использовал, не был потокобезопасным. Я исправил его, и теперь я уверен, что новая библиотека потокобезопасна.

Спасибо за вашу помощь и предложения.

1

Одним из свойств хорошего модульного испытания является то, что его необходимо повторять. Каждый раз, когда он запускается, он должен работать так же, как и раньше. Это просто невозможно при нарезке. Например, тест может работать должным образом 999 раз, но один раз зашел в тупик. Значение потока не только бесполезно, но и дает вам ложное чувство уверенности в том, что ваш код на самом деле не имеет взаимоблокировки.

Для безопасности тестирования заправочной, существует несколько других способов сделать это:

Мок из пронизывающих

Extract прочь резьбовую код и заменить его абстракции, которые могут быть издевались в тесте. Таким образом, единичный тестовый код будет имитировать несколько потоков без самой потоковой передачи. Но это требует, чтобы вы знали, что все возможные пути потоков могут проходить через ваш код, делая его бесполезным для чего-либо сложного.

Этому также может помочь использование неизменных структур данных и чистых методов. Затем проверенный код ограничен небольшой частью кода, обеспечивающей синхронизацию между потоками.

Endurance тестирование

Дизайн тест, чтобы работать в течение очень длительного периода времени, порождая новые темы и вызывающий код все время. Если он работает в течение нескольких часов без взаимоблокировки, вы получаете уверенность, что нет тупика. Это не может быть выполнено как часть обычного набора тестов, и это не дает вам 100% уверенности. Но это гораздо более практично, чем пытаться перечислить все возможные способы взаимодействия потоков.

+0

+1. Мое предположение о downvotes заключается в том, что OP _might_ знает нижние части потоков в модульных тестах уже, и он спросил конкретно о том, как тестировать _threaded_, - который сказал, что ваше объяснение *, почему это плохо, заслуживает внимания. –

+0

Спасибо за предложения. Я думаю, что вариант тестирования Endurance будет работать для нас, поскольку все, что я хочу, это доказать, что мой код является потокобезопасным (я обновил свой вопрос). Но чтобы доказать это, мне нужно показать результаты в моем выпуске, поэтому я создал обработчик. Эта часть, похоже, не работает. –

+0

@PaulMeems. Вы не можете доказать, что ваш код является потокобезопасным, пока вы не используете какой-то формальный метод. Для чего потребуется совершенно другой подход, чем вы сейчас делаете. – Euphoric

Смежные вопросы