Как обрабатывать исключения, возникающие в других потоках при модульном тестировании?
При тестировании с помощью Visual Studio Team Test необработанные исключения в тестах попадают и сообщаются в результатах. Поэтому я был удивлен, увидев сбой процесса тестового хостинга (VSTestHost.exe) и показывая диалоговое окно сбоя системы.
После дальнейшего расследования этот сбой был необработанным исключением, поднятым в другом потоке (более прямо, это был обратный вызов асинхронного сокета). И действительно, что-то вроде этого нарушает процесс хостинга:
[TestMethod]
void Test()
{
new Thread(() => { throw new Exception(); }).Start();
}
Любые советы, что я должен там делать?
- Должен ли я просто жить с ним, говоря, что любой распространенный/зарегистрированный код должен быть протестирован хотя бы один раз, и поэтому такие вещи, скорее всего, поймаются?
- Должен ли я попытаться установить глобальный обработчик исключений и проверить его статус в каждом методе сбрасывания?
- Или, может быть, уже есть вещи, помогающие с этим?
Ответы
Ответ 1
Вы можете использовать обработчик Global Exception, чтобы поймать все неперехваченное исключение в AppDomain:
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new EventHandler(UnhandledExceptionHandler);
Я думаю, что это сработает для исключений, брошенных из других потоков.
Ответ 2
Вы можете попробовать установить AppDomain.CurrentDomain.UnhandledException
и посмотреть, работает ли это? Я не знаю, как это взаимодействует с тест-тестированием VS
Ответ 3
Как правило, я стараюсь избегать запуска потоков в модульных тестах, введя поставщика потоков, который во время модульных тестов фактически не обеспечивает поток, а выполняет синхронно.
Конечно, с таким подходом вы не можете протестировать какие-либо потенциальные конфликты или два реальных пути кода, идущие параллельно, но есть хороший аргумент, который должен быть сделан, что такой тест на самом деле не является unit test.
Когда вы тестируете два одновременных потока, вам нужен поставщик потоков, который ловит исключения в конце потока и сообщает об ошибках.
Ответ 4
Несколько идей:
- Используйте BackgroundWorker для работы unit test. BackgroundWorker автоматически поймает необработанные исключения и сообщит об этом в свойстве Error для RunWorkerCompletedEventArgs. Однако вам понадобится способ заблокировать поток unit test до тех пор, пока BackgroundWorker не завершит работу.
- Этот вариант не является хорошим вариантом и может даже не подходить для ваших целей тестирования. Тем не менее я хотел бы упомянуть. Вы можете вернуться к тому, как необработанные исключения из других потоков обрабатывались в .NET 1.0 и 1.1 с помощью legacyUnhandledExceptionPolicy. До .NET 2.0 необработанные исключения из потоков были спокойно проигнорированы. Однако в .NET 2.0 они фактически приводят к завершению работы приложения. Параметр legacyUnhandledExceptionPolicy позволяет вам иметь поведение перед .NET 2.0.
Ответ 5
То, что я могу сказать из области С++ (если знание может быть переведено), состоит в том, что любое исключение, созданное в данном потоке, может быть поймано в этом потоке. Если ваше основное приложение запускает три других потока, то вам нужно поймать исключения из каждого из (сейчас 4) потоков независимо. Не существует способа реализовать "глобальный" обработчик исключений в потоковом коде. Это связано с тем, как потоки (и процессы) реализованы в ОС.
Но, как я уже сказал, я не знаю, насколько тесно эта логика переводится на С#, потому что она работает поверх VM, как Java.
Ответ 6
Наличие более чем одного потока в ваших тестах не очень подходит для меня. Есть ли что-нибудь о тестируемой логике, которая требует, чтобы было несколько потоков? В самом деле?
Или это так, что есть некоторые соавторы тестируемого кода, которые, случается, многопоточны (например, сокет)? Если это так, вы действительно должны заменить этих коллаборационистов на какие-то тестовые двойники (mocks, stub или что-то еще). Дополнительным преимуществом использования двойных тестов является то, что вы можете контролировать их поведение и ответы легче, чем могли, если бы они были реальными.