Два способа тестирования функций, взаимодействующих с операционной системой
Тестирование является критическим аспектом для поддержания надежного и надежного приложения, независимо от используемого языка или технологии. Это делает код менее уязвимым для ошибок и не дает нам сломать уже существующие функции. В противном случае мы узнали бы о таких ошибках только после того, как с ними столкнулись многие пользователи.
В идеале мы должны попытаться получить максимально возможное покрытие тестами. В приложениях, где мы должны взаимодействовать с операционной системой, нам нужно будет уделить особое внимание настройке теста, поскольку для этого требуется настройка более конкретной среды.
В этой статье я покажу некоторые способы, которые я обычно использую для настройки модульных тестов функций, взаимодействующих с операционной системой.
Тестирование с реальной операционной системой
Некоторым программам необходимо взаимодействовать с компонентами или артефактами операционной системы, например:
- Переменные среды
- Взаимодействие с файловой системой.
- Локальная сеть
Выполнение тестов в среде операционной системы может вызвать дополнительные проблемы, такие как:
- Проблемы с разрешениями.
- Проблемы с безопасностью
- Настройка сложной среды.
- Необходимость очистки всего в конце.
Эти факторы могут внести ненужную сложность. Поэтому для юнит-тестирования желательно не иметь с ними дело. Есть две основные стратегии, которые я использую, чтобы обойти такие проблемы.
Первая стратегия: работа с абстракциями
Концепция абстракций широко используется в программной инженерии. Когда мы строим абстракцию некоторой сущности, она состоит из четко определенного интерфейса, демонстрирующего поведение, которое позволяет нам взаимодействовать с ней, при этом все несущественные детали реализации скрыты от внешнего мира.
Затем вместо выполнения тестов в реальной операционной системе мы можем создавать макеты, которые работают аналогично с точки зрения клиента.
Представьте, что мы хотим протестировать функцию, которая проверяет, находится ли ключ «версия» в определенной YAML-файл.
Мы можем сформировать следующий интерфейс для взаимодействия с файловой системой:
type FSReader interface { ReadFile(string) ([]byte, error) }
Кроме того, мы можем создать два конкретных типа, реализующих такой интерфейс, один с реальной реализацией, а другой макет для тестирования.
В результате, в случае автоматических тестов, мы можем проверить бизнес-логику внутри этой функции, не беспокоясь о настройке реальных файлов или их очистке. Поскольку фиктивный метод дал бы тот же результат, как если бы мы читали из реального файла.
Вторая стратегия: заглушки
Существует альтернатива использованию макетов и абстракций, известных как заглушки.
Заглушки — это прямая замена некоторой зависимости в коде, это похоже на запуск функции или метода, удаление всей его внутренней логики и замена ее тем, что мы хотим вернуть.
Таким образом, мы можем заставить определенную функцию иметь желаемый эффект, независимо от любого другого фактора, относящегося к ней. Он всегда будет возвращать предопределенный вывод.
В Go такой функционал предоставляет пакет gostub.
В приведенном выше примере вывод функции MultiplyBy10
был заменен функцией-заглушкой, где она возвращает ввод, умноженный на 20, а не на 10, как было написано вначале.
Мы также можем заглушить переменные среды, если мы их используем:
func TestFunctionThatUsesENV(t *testing.T) { stubs := gostub.New() stubs.SetEnv("ENV_VAR", "some_value") defer stubs.Reset() // Some Logic that Uses ENV_VAR // ... }
Вернемся к нашему предыдущему примеру, где мы хотим проверить, можно ли найти ключ version
внутри определенного файла YAML. Вместо создания абстракции и конкретных типов для ее реализации мы могли бы напрямую заглушить функцию, отвечающую за получение файлов из локальной файловой системы.
Для набора модульных тестов мы могли бы заглушить функцию и восстановить ее в конце:
Другие способы использования заглушек или макетов
Я использовал эти инструменты в первую очередь для тестирования взаимодействия с операционной системой в Go. Однако их можно применять и многими другими способами.
Предположим, мы хотим выполнить модульные тесты для API. Вероятно, будут задействованы функции и методы, которые требуют взаимодействия с базой данных или службой электронной почты, это некоторые варианты использования, в которых могут применяться макеты или заглушки.
Заключение и основные выводы
В этой статье мы рассмотрели два способа тестирования функций, взаимодействующих с операционной системой. Кроме того, их также можно применять для тестирования других функций.
- Насмешка состоит в создании абстракций, которые инкапсулируют поведение, необходимое нам в нашей бизнес-логике. Клиент не смог бы отличить структуру с реальным поведением от макета.
- Заглушка состоит в замене логики внутри функции, чтобы она возвращала именно тот результат, который нам нужен, заставляя набор тестов работать так, как мы планировали.
Я надеюсь, что эти советы были полезны! Спасибо за прочтение.