Модульное тестирование многопоточного приложения? - PullRequest
31 голосов
/ 21 сентября 2008

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

Ответы [ 9 ]

27 голосов
/ 26 сентября 2008

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

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

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

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

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

22 голосов
/ 21 сентября 2008

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

  • Недостаток воспроизводимости: тесты будут проваливаться только время от времени, и они не помогут выявить проблемы.
  • Неустойчивая неудачная сборка раздражает всех в команде - потому что последний коммит всегда будет ошибочно подозреваться как причина неудачной сборки.
  • В случае возникновения взаимных блокировок, вероятно, будет зависать сборка до тех пор, пока не встретится тайм-аут выполнения, что может значительно замедлить сборку.
  • Средой сборки, скорее всего, будет среда с одним процессором (например, сборка выполняется на ВМ), в которой проблемы параллелизма могут никогда не возникнуть - независимо от того, сколько времени ожидания установлено.
  • Это как-то отрицает идею иметь простые, изолированные единицы проверяющего кода.
10 голосов
/ 21 сентября 2008

Если вам нужно проверить, что фоновый поток что-то делает, простой метод, который я нахожу удобным, это иметь метод 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));

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

7 голосов
/ 21 сентября 2008

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

Я видел демо на этой неделе на шоу - по-видимому, оно в Альфе (называется Racer)

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

4 голосов
/ 23 марта 2010

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

2 голосов
/ 22 марта 2010

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

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

2 голосов
/ 22 сентября 2008

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

0 голосов
/ 21 сентября 2008

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

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

0 голосов
/ 21 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...