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

Единичное тестирование параллельного программного обеспечения - что вы делаете?

Поскольку программное обеспечение становится все более и более параллельным, как вы справляетесь с тестированием основного поведения типа с помощью своих модульных тестов (а не с параллельным поведением, только с основным поведением)?

В старые добрые времена у вас был тип, вы его назвали, и вы проверили, что он вернул и/или какие другие вещи он назвал.

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

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

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


Edit

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


4b9b3361

Ответ 1

Поле модульных испытаний для условий гонки и тупиков является относительно новым и не имеет хороших инструментов.

Я знаю два таких инструмента как в ранних стадиях альфа/бета:

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

Ответ 2

Отказ от ответственности: я работаю на Corensic, небольшой стартап в Сиэтле. У нас есть инструмент под названием Jinx, который предназначен для обнаружения ошибок concurrency в вашем коде. На данный момент это бесплатно, пока мы в бета-версии, поэтому вы можете проверить это. (http://www.corensic.com/)

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

Jinx работает в Linux и Windows, а также в собственном и управляемом коде. Это язык и прикладная платформа агностик и могут работать со всеми вашими существующими инструментами.

Если вы проверите это, отправьте нам отзыв о том, что работает и не работает. Мы запускаем Jinx на некоторых крупных проектах с открытым исходным кодом и уже видим ситуации, когда Jinx может найти ошибки в 50-100 раз быстрее, чем просто стресс-тестирование.

Ответ 3

Я рекомендую собрать копию Growing Object Oriented Software от Freeman and Pryce. Последние пара глав очень поучительны и касаются этой конкретной темы. Он также вводит некоторую терминологию, которая помогает фиксировать обозначение для обсуждения.

Подводя итог.... Основная идея состоит в том, чтобы разделить функциональность и параллельные/синхронизирующие аспекты.

  • Первый тест-драйв функциональной части в одном синхронном потоке, таком как обычный объект.
  • Как только у вас будет закреплена функциональная часть. Вы можете перейти к параллельному аспекту. Для этого вам придется подумать и придумать " наблюдаемые инварианты w.r.t. concurrency" для вашего объекта, например. счетчик должен быть равен временам вызова метода. Как только вы определили инварианты, вы можете написать стресс-тесты, которые запускают несколько потоков et.all, чтобы попытаться сломать ваши инварианты. Стресс-тесты утверждают ваши инварианты.
  • Наконец, в качестве дополнительной защиты запустите инструменты или статический анализ, чтобы найти ошибки.

Для пассивных объектов, то есть кода, который будет вызываться от клиентов на разных потоках: ваш тест должен имитировать клиентов, запустив собственные потоки. Затем вам нужно будет выбрать между прослушиванием уведомлений или выборкой/опросом, чтобы синхронизировать ваши тесты с SUT.

  • Вы можете заблокировать до получения ожидаемого уведомления
  • Опросите некоторые наблюдаемые побочные эффекты с разумным временем ожидания.

Ответ 4

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

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

Ответ 5

В модульных тестах действительно не следует тестировать concurrency/асинхронное поведение, вы должны использовать там mocks и убедиться, что макеты получают ожидаемый ввод.

Для тестов интеграции я просто явно вызываю фоновое задание, а затем проверяю ожидания после этого.

В Cucumber это выглядит так:

When I press "Register"
And the email sending script is run
Then I should have an email

Ответ 6

Учитывая, что ваш TPL будет иметь свой собственный отдельный unit test, вам не нужно проверять это.

Учитывая, что я пишу два теста для каждого модуля:
1) Один threaded unit test, который использует некоторую переменную среды или #define для включения TPL, чтобы я мог проверить свой модуль на функциональную корректность.
2) Стресс-тест, который запускает модуль в поточном режиме развертывания. Этот тест пытается найти проблемы concurrency и должен использовать множество случайных данных.

Второй тест часто включает в себя множество модулей, и, вероятно, это скорее интеграционный/системный тест.