Каковы некоторые стратегии для unit test планировщика?
Это сообщение начиналось как "Каковы некоторые общие шаблоны в модульном тестировании многопоточного кода?", но я нашел несколько других обсуждения, которые в целом согласились с тем, что "это тяжело (TM)" и "It Depends (TM)". Поэтому я подумал, что сокращение объема вопроса будет более полезным.
Фон. Мы внедряем простой планировщик, который дает вам возможность регистрировать обратные вызовы при запуске и остановке заданий и, конечно же, настраивать частоту планирования. В настоящее время мы создаем облегченную оболочку вокруг java.util.Timer.
Аспекты
-
Я не нашел способ протестировать этот планировщик, полагаясь только на общедоступные интерфейсы (что-то вроде addJob(jobSchedule, jobArgs,jobListener)
, removeJob(jobId)
).
-
Как я могу узнать, что задание было вызвано в соответствии с указанным расписанием?
Ответы
Ответ 1
вы можете использовать рекордерный объект, который записывает порядок, тайминги и другие полезные материалы в каждом unit test вашего планировщика. Тест прост:
- создать объект рекордера
- настроить расписание
- выполнить unit test
- проверьте, что объект рекордера "совместим" с расписанием
Ответ 2
Следует помнить также, что вам не нужно проверять, работает ли таймер. Вы можете написать макетную версию таймера (путем расширения класса или использования EasyMock), который просто проверяет, что вы вызываете его правильно, возможно даже заменяя достаточно, чтобы вам вообще не нужны потоки. В этом случае это может быть больше работы, чем необходимо, если у вашего слушателя заданий есть достаточное количество обратных вызовов для отслеживания планировщика.
Другая важная вещь, которую следует помнить, заключается в том, что при тестировании планировщика используйте настраиваемые задания, которые отслеживают работу планировщика; при тестировании запланированных заданий вызывайте обратные вызовы напрямую, а не через планировщик. У вас может быть тест интеграции более высокого уровня, который проверяет оба вместе, в зависимости от системы.
Ответ 3
Существует много режимов сбоев, которые может отобразить такой планировщик, и каждый из них, скорее всего, потребует собственного тестового примера. Эти тестовые примеры, вероятно, будут очень разными, поэтому "это зависит".
Для тестирования параллельного программного обеспечения на Java в целом я рекомендую эту презентацию из JavaOne 2007: Тестирование параллельного программного обеспечения.
Для тестирования того, что планировщик должен выполнять задания в точном соответствии с их графиком, я бы создал абстракцию времени. Я сделал что-то подобное в одном из моих проектов, где у меня есть интерфейс Time или Clock. Реализация по умолчанию будет MillisecondTime, но во время тестирования я переключу ее с помощью TickTime. Эта реализация позволит моему unit test контролировать, когда время продвигается и на сколько.
Таким образом, вы можете написать тест, где задание должно запускаться каждые 10 тиков. Затем ваш тест просто ускоряет счетчик тиков и проверяет, чтобы задания выполнялись с правильными тиками.
Ответ 4
Несколько способов тестирования параллельного кода.
- многократно запускать один и тот же код под нагрузкой, некоторые ошибки появляются только изредка, но могут отображаться последовательно, если они выполняются повторно.
- Сохранять результаты различных потоков/заданий в коллекции, например BlockingQueue. Это позволит вам проверять результаты в текущем потоке и закончить своевременно (без уродливых произвольных заявлений сна).
Если вы находите тестирование concurrency сложным, подумайте о реорганизации ваших объектов/компонентов, чтобы сделать их более легкими для тестирования.
Ответ 5
Если планировщик делегирует Executor
или ExecutorService
для выполнения задач, вы можете использовать Dependency Injection, чтобы удалить прямую зависимость от типа Executor
, и использовать простой однопоточный Executor
чтобы протестировать большую часть функциональности вашего планировщика без усложнения действительно многопоточный код. После того, как вы отладили эти тесты, вы могли бы перейти к более сложному, но теперь существенно уменьшенному по величине, задаче тестирования безопасности потоков.