Моки и мокирование — это техники, используемые в тестировании программного обеспечения, чтобы изолировать код, который тестируется, от других компонентов, с которыми он взаимодействует. Они особенно полезны, когда нужно протестировать код, зависящий от внешних сервисов, баз данных или других сторонних ресурсов. Давайте разберём подробнее.
Что такое моки?
Мок (от англ. mock, «подделка») — это объект, который имитирует поведение реального объекта в приложении, но при этом не содержит логики этого объекта. Моки создают для того, чтобы тестировать компоненты в изоляции от других зависимостей.
Пример: если ваш код взаимодействует с внешним API для получения данных, вы можете использовать мок, чтобы «подделать» этот API и заранее задать ожидаемые ответы. Так вы сможете протестировать логику работы с данными без реального обращения к API.
Что такое мокирование?
Мокирование — это процесс создания моков и настройки их поведения. Мокирование позволяет:
- Контролировать поведение зависимостей: вы можете точно указать, как мок будет себя вести в каждом тесте, какие данные он должен возвращать или какие исключения выбрасывать.
- Изолировать тестируемый код: моки заменяют реальные объекты и предотвращают нежелательные вызовы или побочные эффекты, связанные с ними (например, доступ к базе данных).
- Тестировать различные сценарии: с помощью моков можно легко смоделировать разные ситуации, включая те, которые сложно воспроизвести в реальной среде, например, ошибки сети.
Библиотеки для мокирования в Swift и Kotlin
В Swift и Kotlin также есть популярные библиотеки для мокирования, которые помогают разработчикам тестировать код с использованием заглушек для зависимости. Вот основные из них:
Библиотеки для мокирования в Swift
- Cuckoo
- Одна из самых популярных библиотек для мокирования в Swift.
- Позволяет автоматически генерировать моки для классов и протоколов.
- Поддерживает как синхронные, так и асинхронные методы, удобна для тестирования сложных сценариев.
- Mockito-swift
- Похожая на Java-библиотеку Mockito, но адаптированная для Swift.
- Поддерживает такие функции, как проверка вызовов, создание заглушек для методов и тестирование разных сценариев.
- Простая и удобная в использовании для базовых моков.
- Mockingbird
- Современная и мощная библиотека для мокирования в Swift.
- Поддерживает автоматическую генерацию моков и может интегрироваться с Xcode.
- Обеспечивает гибкость при настройке поведения моков и поддерживает «expectations» для проверки вызовов.
- Sourcery
- Не совсем библиотека для мокирования, но используется в Swift для генерации кода, в том числе моков.
- Подходит для создания кастомных заглушек и экономит время за счет генерации кода.
Библиотеки для мокирования в Kotlin
- Mockito-Kotlin
- Версия популярной Java-библиотеки Mockito, адаптированная для Kotlin.
- Позволяет легко создавать моки, настраивать поведение функций и проверять их вызовы.
- Хорошо интегрируется с Kotlin-кодом и поддерживает котлин-специфические функции, такие как nullable типы и расширения.
- MockK
- Мощная библиотека для мокирования, специально разработанная для Kotlin.
- Поддерживает объектные моки (например, для Singleton и объектов-компаньонов), что отличает её от многих других библиотек.
- Позволяет легко создавать заглушки, настраивать возвращаемые значения и проверять вызовы.
- Удобна для работы с Kotlin-коррутинами, что особенно важно в современных Android-приложениях.
- Kotlin Test
- Встроенная библиотека тестирования для Kotlin, которая также поддерживает создание простых моков и заглушек.
- Подходит для базового мокирования, но для более сложных случаев лучше использовать MockK или Mockito-Kotlin.
Эти библиотеки позволяют в Swift и Kotlin эффективно создавать тесты, изолируя тестируемый код от зависимостей и повышая надёжность результатов.
Является ли мокирование антипаттерном
Мокирование само по себе не является антипаттерном, однако неправильное или чрезмерное использование моков может привести к проблемам и стать антипаттерном. В некоторых случаях это снижает качество тестов и делает их менее полезными. Давайте разберём, когда мокирование может считаться антипаттерном и в чём заключаются риски его неправильного применения.
Когда мокирование полезно
Мокирование оправдано, когда:
- Нужно изолировать код от внешних зависимостей, таких как базы данных, сети, API и пр.
- Хотите протестировать поведение кода в разных сценариях, включая ошибки и исключения.
- Есть необходимость ускорить выполнение тестов, исключив сложные и долгие зависимости.
- Нужно проверить отдельные методы или классы, которые зависят от других сложных компонентов.
В этих случаях моки помогают сделать тесты более предсказуемыми, быстрыми и устойчивыми.
Когда мокирование становится антипаттерном
Мокирование может считаться антипаттерном, если:
- Чрезмерное использование моков: когда моки используются слишком часто или для всех зависимостей, это может привести к созданию тестов, которые проверяют не реальное поведение системы, а только согласованность моков.
- Мокирование сложной логики: если моки создаются для компонентов с сложной бизнес-логикой, вы фактически подменяете настоящие данные «бутафорией». Это может привести к ситуации, когда тесты проходят успешно, но реальные ошибки остаются незамеченными.
- Сложные и хрупкие тесты: если моки создаются с жёсткой привязкой к внутренним деталям реализации, при изменении этих деталей тесты начинают ломаться. Это делает тесты «хрупкими» и трудно поддерживаемыми.
- Усложнение поддержки тестов: если моки активно настраиваются с помощью сложных заглушек и проверок, тесты становятся трудночитаемыми и запутанными. Это затрудняет их поддержку и усложняет понимание.
- Недостаточное тестирование интеграции: чрезмерное увлечение моками приводит к недостатку интеграционных тестов, в которых проверяется взаимодействие компонентов. В результате, даже если юнит-тесты проходят, система может оказаться неработоспособной при реальной интеграции.
Лучшие практики использования мокирования
Чтобы избежать превращения мокирования в антипаттерн:
- Используйте моки только там, где это необходимо. Например, для изоляции внешних ресурсов, а не для подмены внутренней логики приложения.
- Пишите интеграционные тесты для проверки работы реальных компонентов вместе. Юнит-тесты с моками не должны заменять интеграционные тесты.
- Тестируйте поведение, а не реализацию: фокусируйтесь на том, что код должен делать, а не на том, как он это делает. Это снизит зависимость от внутренних деталей реализации.
- Минимизируйте количество моков в одном тесте: старайтесь держать каждый тест максимально простым и понятным, чтобы при изменении зависимостей не нужно было менять весь тест.
Таким образом, мокирование может быть мощным инструментом, если использовать его разумно. Проблемы начинаются, когда моки используются без необходимости или в слишком большом количестве — в этом случае тесты могут становиться антипаттерном.