Поскольку программное обеспечение становится более параллельным, как Вы обрабатываете тестирование базового поведения типа с Вашими модульными тестами (не параллельное поведение, просто базовое поведение)?
В добрые старые времена у Вас был тип, Вы назвали его, и Вы проверили также, что он возвратил и/или что другие вещи он назвал.
В наше время Вы называете метод, и фактическая работа, как планируют, будет работать на следующем доступном потоке; Вы не знаете, когда это на самом деле запустит и назовет другие вещи - и кроме того, те другие вещи могли быть параллельными также.
Как Вы имеете дело с этим? Вы абстрагируете/вводите параллельный планировщик (например, абстрагируете Библиотеку Параллели Задачи и обеспечиваете фальшивку/насмешку в модульных тестах)?
С какими ресурсами Вы столкнулись, который помог Вам?
Править
Я отредактировал вопрос подчеркнуть тестирование нормального поведения типа (игнорирующий любой параллельный механизм, используется для использования в своих интересах многоядерный, например, TPL),
Область модульного тестирования на условия гонки и тупики относительно новая и не имеет хороших инструментов.
Я знаю два таких инструмента, оба на ранних стадиях альфа/бета:
Другой вариант - попытаться написать "стресс-тест", который вызовет тупики/условия гонки, создать несколько экземпляров/потоков и запустить их бок о бок. Недостатком такого подхода является то, что в случае неудачи теста будет очень сложно ее воспроизвести. Я предлагаю использовать логи как в тестовом, так и в рабочем коде, чтобы вы могли понять, что произошло.
Модульные тесты действительно не должны тестировать параллелизм / асинхронное поведение, вы должны использовать там макеты и проверять, получают ли макеты ожидаемый ввод.
Для интеграционных тестов я просто явно вызываю фоновую задачу, а затем проверяю ожидания после этого.
В Cucumber это выглядит так:
When I press "Register"
And the email sending script is run
Then I should have an email
Техника, которую я нашел полезной, заключается в запуске тестов в инструменте, который обнаруживает условия гонки, например Intel Parallel Inspector. Тест выполняется гораздо медленнее, чем обычно, поскольку необходимо проверять зависимости от времени, но за один прогон можно найти ошибки, которые в противном случае потребовали бы миллионов повторных обычных прогонов.
Я нашел это очень полезным при преобразовании существующих систем для тонкого параллелизма с помощью многоядерности.
Отказ от ответственности: Я работаю в Corensic, небольшом стартапе в Сиэтле. У нас есть инструмент под названием Jinx, который предназначен для обнаружения ошибок параллелизма в вашем коде. Пока мы находимся в бета-версии, он бесплатен, так что вы, возможно, захотите проверить его. ( http://www.corensic.com/ )
В двух словах, Jinx - это очень тонкий гипервизор, который при активации проскальзывает между процессором и операционной системой. Затем Jinx интеллектуально берет фрагменты выполнения и запускает симуляцию различных таймингов потоков для поиска ошибок. Когда мы находим определенное время выполнения потока, которое приводит к возникновению ошибки, мы делаем это время "реальностью" на вашей машине (например, если вы используете Visual Studio, отладчик остановится в этой точке). Затем мы указываем область в вашем коде, где была вызвана ошибка. В Jinx нет ложных срабатываний. Если он обнаруживает ошибку, то это точно ошибка.
Jinx работает в Linux и Windows, как в родном, так и в управляемом коде. Он не зависит от языка и платформы приложения и может работать со всеми существующими инструментами.
Если вы попробуете его, пожалуйста, пришлите нам отзывы о том, что работает и не работает. Мы запустили Jinx в некоторых крупных проектах с открытым исходным кодом и уже видим ситуации, когда Jinx может найти ошибки в 50-100 раз быстрее, чем простое стресс-тестирование кода.
Я рекомендую взять копию Growing Object Oriented Software от Freeman and Pryce . Последние несколько глав очень поучительны и посвящены этой конкретной теме. Он также вводит некоторую терминологию, которая помогает определить обозначения для обсуждения.
Подводя итог .... Их основная идея состоит в том, чтобы разделить функциональные возможности и аспекты параллелизма / синхронизации .
Для пассивных объектов, то есть кода, который будет вызываться от клиентов в разных потоках: ваш тест должен имитировать клиентов, запуская свои собственные потоки. Затем вам нужно будет выбрать между прослушиванием уведомлений или методом выборки / опроса для синхронизации ваших тестов с SUT.
Учитывая, что ваш TPL будет иметь свой отдельный юнит-тест, вам не нужно проверять это.
Учитывая, что я пишу два теста для каждого модуля:
1) Однопоточный модульный тест, который использует некоторую переменную окружения или #define для включения TPL, чтобы я мог проверить свой модуль на функциональную корректность.
2) Стресс-тест, который запускает модуль в его потоковом развертываемом режиме. Этот тест пытается найти проблемы параллелизма и должен использовать много случайных данных.
Второй тест часто включает много модулей и поэтому, вероятно, является скорее интеграционным/системным тестом.