Как моделировать concurrency в модульных тестах?

Я новичок в модульном тестировании и в настоящее время немного экспериментирую с инструментами тестирования Visual Studio.

Мой вопрос - как определить утверждения о параллельном поведении в этих тестах. Например. если класс BoundedChan<T> реализует ограниченный канал, как я могу указать тесты, например

  • "channel.Send не будет блокировать" или
  • "Если пропускная способность канала превышена, channel.Send будет блокироваться до тех пор, пока не будет прочитано значение"

Есть ли элегантное решение для написания этих утверждений?

Ответы

Ответ 1

Здесь статья в блоге, в которой обсуждается тема; он фокусируется на NUnit.

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

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

Ответ 2

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

В общем, я бы не рекомендовал добавлять модульные тесты к вашим классам для параллельных сценариев. С практикой, я думаю, вы узнаете, что автоматические модульные тесты имеют один или несколько "сладких пятен", и что сосредоточение усилий в этих областях (и использование других практик в других областях) дает более высокую рентабельность инвестиций.

Но ваш класс, по-видимому, около concurrency (не может точно определить на основе предоставленной информации), и поэтому это может быть случай, когда вам действительно нужен тест concurrency -simulating.

Вы можете (насколько мне известно) запустить несколько потоков в unit test, если хотите. Но может быть и более простой способ. Подумайте об использовании шаблона Compose Method. Хотя речь идет о предмете - эта схема довольно важна для эффективных модульных тестов для всех, а не только с помощью concurrency.

Ответ 3

Я создал помощника, который может запустить кучу потоков для выполнения одновременных действий. Помощник предоставляет примитивы синхронизации и механизмы регистрации. Его можно использовать в сочетании с любой инфраструктурой unit test. Вот фрагмент кода из unit test:

[Test]
public void TwoCodeBlocksInParallelTest()
{
    // This static method runs the provided Action delegates in parallel using threads
    CTestHelper.Run(
        c =>
            {
                Thread.Sleep(1000); // Here should be the code to provide something 
                CTestHelper.AddSequenceStep("Provide"); // We record a sequence step for the expectations after the test
                CTestHelper.SetEvent();
            },
        c =>
            {
                CTestHelper.WaitEvent(); // We wait until we can consume what is provided
                CTestHelper.AddSequenceStep("Consume"); // We record a sequence step for the expectations after the test
            },
        TimeSpan.FromSeconds(10)); // This is a timeout parameter, if the threads are deadlocked or take too long, the threads are terminated and a timeout exception is thrown 

    // After Run() completes we can analyze if the recorded sequence steps are in the correct order
    Expect(CTestHelper.GetSequence(), Is.EqualTo(new[] { "Provide", "Consume" }));
}

Он может использоваться для тестирования клиента/сервера или синхронизации в компонентах или просто запуска потока с таймаутом. Я продолжу улучшать это в ближайшие недели. Вот страница проекта: Concurrency Помощник по тестированию