Шаг первый - понять, что часто большая часть кода, необходимого для модульного тестирования, ортогональна потоку. Это означает, что вы должны попытаться разбить код, выполняющий работу, из кода, выполняющего потоки. Как только это будет сделано, вы можете легко протестировать код, который выполняет работу, используя обычные методы модульного тестирования. Но, конечно, ты это знал.
Тогда проблема заключается в тестировании потоковой стороны проблемы, но, по крайней мере, теперь у вас есть точка, в которой эта потоковая среда взаимодействует с кодом, который выполняет работу, и, надеюсь, у вас есть интерфейс, который вы можете смоделировать. Теперь, когда у вас есть макет для кода, который вызывает многопоточный код, я считаю, что лучше всего добавить некоторые события в макет (это может означать, что вам нужно вручную свернуть макет). Затем события будут использоваться, чтобы позволить тесту синхронизироваться и блокировать тестируемый код потока.
Так, например, допустим, у нас есть что-то действительно простое, многопоточная очередь, которая обрабатывает рабочие элементы. Вы бы издевались над работой. Интерфейс может включать метод «Process ()», который поток вызывает для выполнения работы. Вы бы поместили там два события. Тот, который макет устанавливает при вызове Process (), и тот, который макет ожидает после того, как он установил первое событие. Теперь в своем тесте вы можете запустить свою очередь, опубликовать фиктивный рабочий элемент, а затем дождаться события рабочего элемента «Я обрабатываю». Если все, что вы тестируете, это то, что процесс вызывается, тогда вы можете установить другое событие и позволить потоку продолжить. Если вы тестируете что-то более сложное, например, как очередь обрабатывает многократную диспетчеризацию, или что-то еще, то вы можете делать другие вещи (например, отправлять и ждать другие рабочие элементы) перед освобождением потока. Поскольку вы можете подождать с таймаутом в тесте, вы можете убедиться, что (скажем) только два рабочих элемента обрабатываются параллельно, и т. Д., И т. Д. Главное, чтобы вы сделали тесты детерминированными, используя события, на которые потоковый код блокирует что тест может контролировать при запуске.
Я уверен, что ваша ситуация более сложная, но это основной метод, который я использую для тестирования многопоточного кода, и он работает довольно хорошо. Вы можете получить удивительный контроль над многопоточным кодом, если макетируете нужные биты и вводите точки синхронизации.
Вот еще немного информации о подобных вещах, хотя речь идет о кодовой базе C ++: http://www.lenholgate.com/blog/2004/05/practical-testing.html