Подтвердить что ты не робот

Единичное тестирование многопоточного приложения?

Есть ли у кого-нибудь советы для последовательного способа использования unit test многопоточного приложения? Я сделал одно приложение, в котором наши маточные "рабочие потоки" имели thread.sleep с временем, которое было определено переменной открытого участника. Мы использовали бы это, чтобы мы могли установить, как долго будет выполняться конкретный поток для завершения его работы, тогда мы могли бы сделать наши утверждения. Любые идеи лучшего способа сделать это? Любые хорошие макеты для .Net, которые могут справиться с этим?

4b9b3361

Ответ 1

Мой совет заключается не в том, чтобы полагаться на модульные тесты для обнаружения проблем concurrency по нескольким причинам:

  • Отсутствие воспроизводимости: тесты будут терпеть неудачу только раз в то время, и не будут очень полезны для выявления проблем.
  • Неудачная сборка сбоев будет раздражать всех в команде - потому что последняя фиксация всегда будет ошибочно заподозрена в причине сбоя сборки.
  • Тупики, которые могут возникнуть, могут заблокировать сборку до тех пор, пока не будет обнаружен тайм-аут выполнения, который может значительно замедлить сборку.
  • Среда сборки, вероятно, будет единой средой процессора (думаю, что сборка выполняется в виртуальной машине), где проблемы concurrency никогда не могут произойти - независимо от того, сколько времени сна установлено.
  • Это как-то побеждает идея иметь простые, изолированные единицы проверяющего кода.

Ответ 2

Шаг первый заключается в том, чтобы понять, что часто код, необходимый для unit test, ортогонален потоку. Это означает, что вы должны попытаться разбить код, который выполняет работу с кодом, который выполняет потоки. После этого вы можете легко протестировать код, который выполняет эту работу, используя обычные методы тестирования модулей. Но, конечно же, вы это знали.

Проблема заключается в том, чтобы тестировать проблему с потоками проблемы, но по крайней мере теперь у вас есть точка, в которой эта потоковая обработка взаимодействует с кодом, который выполняет эту работу, и, надеюсь, у вас есть интерфейс, который вы можете высмеять. Теперь, когда у вас есть макет кода, который вызывает код потока, я нахожу лучшее, что нужно сделать, это добавить некоторые события в макет (это может означать, что вам нужно вручную перевернуть ваш макет). Затем события будут использоваться, чтобы позволить тесту синхронизировать и блокировать тестируемый код потоковой передачи.

Так, например, скажем, у нас есть что-то действительно простая, многопоточная очередь, обрабатывающая рабочие элементы. Вы издеваетесь над рабочим элементом. Интерфейс может включать в себя метод "Process()", который поток вызывает для выполнения работы. Вы бы поставили там два события. То, что макет устанавливает, когда вызывается Process(), и тот, который макет ждет после того, как он установил первое событие. Теперь, в своем тесте, вы можете запустить свою очередь, отправить макет рабочего элемента, а затем подождать на рабочем элементе "Я обрабатываюсь". Если все, что вы тестируете, это вызов процесса, вы можете установить другое событие и продолжить поток. Если вы тестируете что-то более сложное, например, как очередь обрабатывает множественную отправку или что-то в этом роде, вы можете делать другие вещи (например, сообщение и ждать других рабочих элементов) до выпуска потока. Поскольку вы можете ждать с тайм-аутом в тесте, вы можете убедиться, что (скажем) обрабатываются только два рабочих элемента параллельно и т.д. И т.д. Главное, что вы делаете тесты детерминированными с использованием событий, что тест может контролировать, когда они запускаются.

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

Вот еще информация об этом, хотя речь идет о кодовой базе С++: http://www.lenholgate.com/blog/2004/05/practical-testing.html

Ответ 3

Если вам нужно проверить, что фоновый поток что-то делает, простой метод, который мне удобен, состоит в том, чтобы иметь метод WaitUntilTrue, который выглядит примерно так:

bool WaitUntilTrue(Func<bool> func,
              int timeoutInMillis,
              int timeBetweenChecksMillis)
{
    Stopwatch stopwatch = Stopwatch.StartNew();

    while(stopwatch.ElapsedMilliseconds < timeoutInMillis)
    {
        if (func())
            return true;
        Thread.Sleep(timeBetweenChecksMillis);
    }   
    return false;
}

Используется следующим образом:

volatile bool backgroundThreadHasFinished = false;
//run your multithreaded test and make sure the thread sets the above variable.

Assert.IsTrue(WaitUntilTrue(x => backgroundThreadHasFinished, 1000, 10));

Таким образом, вам не нужно долго спать со своей основной тестовой нитью, чтобы завершить фоновое время. Если фон не заканчивается в течение разумного промежутка времени, тест не выполняется.

Ответ 4

TypeMock (коммерческий) имеет единую структуру тестирования, которая автоматически пытается найти взаимоблокировки в многопоточных приложениях, и я думаю, что можно настроить для запуска потоков с предсказуемым переключением контекста.

Я видел демо на этой неделе в шоу - по-видимому, это в Alpha (называемый Racer)

http://www.typemock.com/Typemock_software_development_tools.html

Ответ 5

Я наткнулся на исследовательский продукт, называемый Microsoft Chess. Он специально разработан для не детерминированного тестирования многопоточных приложений. Недостатком до сих пор является то, что он интегрирован в VS.

Ответ 6

Важно проверить многопоточный код на многопроцессорной машине. Недостаточно двухъядерной машины. Я видел взаимоблокировки на 4-процессорной машине, которая не встречалась на двухъядерном одном процессоре. Затем вам нужно создать стресс-тест на основе клиентской программы, которая порождает множество потоков и делает несколько запросов против целевого приложения. Это помогает, если клиентская машина также является многопроцессорной, поэтому на целевое приложение больше нагрузки.

Ответ 7

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

Я портировал библиотеку multithreadedTC с Java на .NET и называл ее TickingTest. Он позволяет запускать несколько потоков из метода unit test и координировать их. У него нет всех особенностей оригинала, но я нашел его полезным. Самое главное, что он отсутствует, это возможность отслеживать потоки, запущенные во время теста.

Ответ 8

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

Ответ 9

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

Однако у вас есть компания с этим... Я предлагаю искать в архивах группы testdrivendevelopment yahoo. Я помню несколько сообщений поблизости. Здесь один из новых. (Если бы кто-то был достаточно любезен, чтобы стесниться и перефразировать.. это было бы здорово. Я слишком сонлив. Нужно ЛО СОСТОЯТЬ)