Код модульного тестирования с непредсказуемыми внешними зависимостями - PullRequest
6 голосов
/ 02 ноября 2011

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

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

Теперь мой собственный код в основном не о реальных операциях на инструментах, а о запуске операций, проверке того, что все в порядке, и синхронизации между ними. Он написан на Java с использованием различных библиотек для взаимодействия с инструментами и их драйверами.

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

  • только тестирование с подключенными реальными приборами, возможно, наиболее точный метод, но он вообще не практичен (вставьте пластину в считыватель, запустите модульный тест, удалите пластинку, запустите модульный тест и т. Д.), Не говоря уже о потенциально опасно,
  • использовать фиктивный объект для имитации части, которая фактически общается с вещью; хотя этот способ определенно проще реализовать (и запустить), он может не воспроизводить весь спектр потенциальных сбоев (как уже упоминалось выше, многое недокументировано, что иногда может вызывать неприятные сюрпризы)

Пока я думаю о том, чтобы пойти с последним, я что-то упустил? Есть ли лучший способ сделать это?

Ответы [ 3 ]

6 голосов
/ 02 ноября 2011

Обе две маркированные точки являются допустимыми вариантами, но каждая из них представляет два различных типа тестирования.

На очень высоком уровне использование фиктивных объектов (для вашей второй маркированной точки) отлично подходит для модульного тестирования -- который просто тестирует ваш код (то есть тестируемая система или SUT), и ничего более постороннего для него.Любые другие зависимости удаляются.Затем вы можете написать контрольные примеры, чтобы выбросить как можно больше различных состояний ошибки (а также, конечно, протестировать «счастливый путь»).Тот факт, что ваша область состояний ошибок недокументирована, вызывает сожаление, и вам следует постараться как можно лучше сократить это.Каждый раз, когда вы сталкиваетесь с новым состоянием ошибки с фактическим внешним устройством, вы должны выяснить, как воспроизвести его с помощью кода, а затем написать еще один новый модульный тест для воссоздания этого условия через вашу фиктивную среду.

Далее,тестирование с подключенными инструментами (согласно первому пункту) отлично подходит для интеграционного тестирования, которое больше тестирует ваш код наряду с фактическими внешними зависимостями.

В общем, модульное тестирование должно быть быстрым (в идеале, под10 минут, чтобы скомпилировать код и запустить весь комплект модульных тестов.) Это означает, что вы быстро получите обратную связь от ваших модульных тестов, если любой написанный вами новый код вызовет сбой любых тестов.Интеграционное тестирование по своей природе может занять больше времени (если, например, одному из ваших внешних устройств требуется 1 минута для вычисления результата или выполнения задачи, и у вас есть 15 различных наборов входных данных, которые вы тестируете, это правильно, 15 минут)для одного небольшого набора тестов.) Ваш CI-сервер (у вас должен быть один из тех, который автоматически компилирует и запускает все тесты) должен автоматически запускаться при фиксации в вашем репозитории управления исходным кодом.Он должен скомпилировать и запустить модульные тесты за один шаг.После того, как эта часть выполнена, она должна предоставить вам обратную связь (хорошую или плохую), а затем, если модульные тесты пройдут успешно, она автоматически запустит ваши интеграционные тесты.Это предполагает, что к вашему серверу CI подключено либо реальное устройство, либо подходящая замена (что бы это ни значило в вашей конкретной среде).

Надеюсь, это поможет.

4 голосов
/ 02 ноября 2011

Если вы используете mocks, то вы можете заменить разные mocks, чтобы работать по-разному. То есть ваши тесты будут согласованы. Это ценно, поскольку запуск тестов на случайно работающей системе не даст вам ощущения безопасности. Каждый прогон может / будет выполнять другой путь кода.

Поскольку вы заранее не знаете всех сценариев отказов, я думаю, что есть два (неисключительных) сценария:

  1. Захватите детали этих сбоев так, как вы их видите, и закодируйте дальнейшие тесты в своих макетах, чтобы повторить их. Следовательно, ваша регистрация должна быть надежной, чтобы фиксировать детали отказа. Со временем ваш набор тестов расширится, чтобы охватить и регрессировать эти сценарии.
  2. Ваши интерфейсы к этим системам могут быть в состоянии перехватить все ошибки, но представить их в ограниченном подмножестве ошибок. например классифицируйте все ошибки, скажем, как ошибки подключения, тайм-ауты и т. д. Таким образом, вы ограничиваете свои сценарии небольшим набором сбоев. К сожалению, я не знаю, практично ли это для вашего приложения.
3 голосов
/ 02 ноября 2011

Вы не можете по отдельности протестировать то, чего не ожидали, по определению.

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

Разделите зависимости, чтобы вы могли создать фальшивый инструмент, а затем сделать так, чтобы он имитировал реальность всеми возможными способами. По мере того, как ваше понимание реальности улучшается, обновите фальшивку и добавьте тесты, чтобы справиться с этой ситуацией. (В некоторых случаях может быть уместным издевательство, в других - притворство.)

...