Включите исполнение JavaScript в браузере, чтобы запустить приложение.
29 окт 2024

Что такое Mock-тестирование, для чего нужно и как правильно проводить

Что такое Mock-тестирование: расскажем подробнее про моки и стабы. ✅ Преимущества и недостатки mock-тестов и когда стоит их использовать. ✅ Список инструментов для mock-тестирования. ✅ Как правильно проводить тестирование — читайте в нашей статье.
  1. Что такое Mock-тестирование
  2. Что такое мок
  3. Что такое стабы
  4. Когда стоит использовать Mock-тестирование
  5. Изоляция зависимостей
  6. Негативное тестирование
  7. Тестирование еще не готовых компонентов
  8. Тестирование взаимодействия
  9. Тестирование кэширования и времени
  10. Тестирование масштабируемости
  11. Тестирование конфигураций
  12. Преимущества и недостатки
  13. Инструменты для mock-тестирования
  14. Unittest
  15. Pytest.mock
  16. Postman
  17. Как провести mock-тестирование

Что такое mock-тестирование

Mock-тестирование — это методика тестирования программы, основанная на замене реальных компонентов системы на их имитации. Такая методика позволяет сосредоточиться на проверке корректности работы отдельных компонентов системы, изолируя ее от зависимостей. То есть не затрагивая какие-либо реальные базы данных, внешние сервисы и другие системы. Моки и стабы — это два основополагающих вида тестовых объектов. Рассмотрим их подробнее.

Что такое мок

Мок — это объект, который имитирует поведение реального компонента системы во время тестирования, однако действия этого объекта полностью контролируются тестом. Другими словами, помимо имитации реальных компонентов, моки позволяют задать результаты вызова того или иного метода, а также убедиться в том, что тестируемый код взаимодействует с зависимостями так, как это ожидается.

Что такое стабы

Стабы — это тоже тестовые объекты, но они возвращают заранее определенные значения на вызовы методов. То есть стабы просто выполняют роль имитируемого объекта: формируют запросы к сервису и возвращают нужные (уже определенные) значения, при этом не отслеживая взаимодействие с зависимостями, как это делают моки. Стабы могут использоваться, когда поведение зависимости не оказывает сильного влияния на тестируемый компонент.

Когда стоит использовать mock-тестирование

В общем случае к mock-тестированию стоит прибегать только в тех случаях, когда необходимо имитировать поведение зависимостей, не являющихся непосредственно частью самого приложения и не контролируемых разработчиками. Что это значит? Если программа взаимодействует, например, со сторонним почтовым сервером, то эту зависимость можно заменить mock-объектом, так как данный почтовый сервер — это не компонент тестируемой программы и он не контролируется ее разработчиками. В случае же, если почтовый сервер по какой-то причине является одним из компонентов приложения, заменять его на mock-объект нежелательно. Ниже рассмотрим ситуации, в которых может применяться mock-тестирование более подробно.

Изоляция зависимостей 

Изоляция зависимостей — одна из основных целей проведения mock-тестирования. Но для чего вообще нужно изолировать зависимости? Дело в том, что часто необходимо протестировать непосредственно то, как работает компонент программы, а не то, как работает база данных, сторонние API или любая другая система, с которой этот компонент взаимодействует. В таком случае mock-объекты здорово выручают, так как позволяют сконцентрироваться на тестируемом компоненте программы.

Негативное тестирование

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

Тестирование еще не готовых компонентов

Mock-объекты также позволяют имитировать поведение компонентов, которые пока еще недоступны для использования, например, находятся в разработке. При таком подходе можно начать тестирование системы заранее и оптимизировать работу либо отдельных программистов, либо команд разработчиков — отдельный специалист или целая команда могут протестировать свои компоненты и продолжать работу.

Тестирование взаимодействия

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

Тестирование кэширования и времени

Целью тестирования кэширования является проверка того, что данные, которые должны быть закэшированы, сохраняются в кэше, а также вовремя обновляются и при необходимости удаляются. Mock-объекты позволяют контролировать время и имитировать кэшированные данные, благодаря чему могут использоваться при проведении такого вида тестирования.

Тестирование масштабируемости

Тестирование масштабируемости — это тип нефункционального тестирования, позволяющий определить, как ведет себя программа при изменении количества пользователей и пользовательских запросов, а также объема данных. Тестирование масштабируемости может проводиться с использованием mock-объектов, так как они позволяют имитировать изменение такого рода нагрузки и, соответственно, отслеживать то, как ведет себя программа.

Тестирование конфигураций 

Конфигурационное тестирование предполагает проверку корректности работы системы при различных конфигурациях, и если каждая конфигурация требует сложной настройки, то проведение такого тестирования может стать долгим и трудоемким процессом. Использование моков позволяет эмулировать различные конфигурации системы, что ускоряет и упрощает тестирование, а также дает возможность создания множества сценариев.

Преимущества и недостатки

Mock-тестирование, как и любая другая методика, имеет свои плюсы и минусы, которые важно учитывать.

Рассмотрим основные преимущества методики:

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

Недостатки:

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

Инструменты для mock-тестирования

Для того чтобы сделать процесс тестирования максимально эффективным и удобным, существуют специализированные инструменты. Ниже будут рассмотрены три инструмента для тестирования на Python: Unittest, Pytest.mock и Postman.

Unittest

Unittest — это модуль, который является частью стандартной библиотеки Python. В unittest входит модуль unittest.mock, который используется для создания mock-объектов — эти объекты позволяют имитировать поведение реальных зависимостей. Mock-объекты, которые создаются с помощью конструктора Mock(), могут эмулировать вызовы методов, возврат значений, отслеживать то, как часто и с какими параметрами вызывались методы и так далее. Все эти возможности позволяют просматривать, как тестируемый код взаимодействует с зависимостями.

Pytest.mock

Pytest.mock — это модуль одной из самых популярных библиотек для тестирования Pytest. По сравнению с unittest этот инструмент является более гибким: предоставляет больше функций и возможностей, среди которых использование фикстур, упрощенный патчинг, поддержка параметризации и возможность детальной настройки поведения моков. Pytest.mock может быть чуть более сложным в изучении, чем unittest как раз из-за необходимости изучения всех дополнительных возможностей, но если во всем разобраться, то Pytest.mock станет незаменимым инструментом для мок-тестирования.

Postman

Postman — это универсальный инструмент для работы с API, с помощью которого можно в том числе тестировать API и создавать HTTP-запросы. Postman также позволяет создавать мок-серверы, которые имитируют работу реального API; тестировать различные сценарии, имитировать задержки и сбои, интегрироваться с другими сервисами для тестирования, если есть такая необходимость. При этом важно понимать, что этот инструмент подходит только для тестирования API, а также имеет ограничение по количеству моков в месяц (если у пользователя нет платной подписки).

Как провести mock-тестирование

Рассмотрим пример проведения mock-тестирования. Допустим, существует какая-то система, где пользователь может зарегистрироваться и получить после этого приветственное SMS:

# registration.py
class SMSService:
    def send_sms(self, phone_number, message):
        # Отправка SMS с использованием внешнего сервиса
        pass
 
class UserRegistration:
    def __init__(self, sms_service):
        self.sms_service = sms_service
 
    def register_user(self, user):
        self.send_welcome_message(user.phone_number)
 
    def send_welcome_message(self, phone_number):
        welcome_message = "Добро пожаловать!"
        self.sms_service.send_sms(phone_number, welcome_message)
py

Чтобы протестировать метод register_user, нужно прибегнуть к использованию реальной системы отправки SMS, но можно создать mock-объект, который заменит объект SMSService:

# test_registration.py
import unittest
from unittest.mock import Mock
from registration import SMSService, UserRegistration
 
class TestUserRegistration(unittest.TestCase):
    def test_register_user(self):
        mock_sms_service = Mock(spec=SMSService)
        registration = UserRegistration(mock_sms_service)       
        test_user = type('User', (), {'phone_number': '75554442211'})()
 
        # Тестируем метод register_user
        registration.register_user(test_user)


        mock_sms_service.send_sms.assert_called_once_with('75554442211', 'Добро пожаловать!') 
        print("Test passed successfully!")
 
# Нужно, если тесты необходимо запустить напрямую
if __name__ == '__main__':
    unittest.main()
py

Здесь также создается экземпляр UserRegistration и тестовый пользователь, а затем проверяется, что метод send_sms был вызван один раз с правильным номером телефона и сообщением. В файл теста обязательно нужно импортировать классы.

Далее необходимо добавить еще несколько тестов для проверки различных сценариев:

 def test_register_user_with_different_number(self):
        mock_sms_service = Mock(spec=SMSService)
        registration = UserRegistration(mock_sms_service)
        test_user = type('User', (), {'phone_number': '74442221100'})()
 
        registration.register_user(test_user)
 
        # Проверяем, что программа корректно работает с другим номером телефона
        mock_sms_service.send_sms.assert_called_once_with('74442221100', 'Добро пожаловать!')
        print("Test passed successfully!")
 
    def test_register_user_failure(self):
        mock_sms_service = Mock(spec=SMSService)
        registration = UserRegistration(mock_sms_service)
        test_user = type('User', (), {'phone_number': '74442221100'})()
 
        registration.register_user(test_user)
 
        # Проверяем, что тест обнаружит ошибку
        mock_sms_service.send_sms.assert_called_once_with('1111111111', 'Добро пожаловать!')
        print("Test failed!")
 
if __name__ == '__main__':
    unittest.main()
bash

Это сильно упрощенный пример mock-тестирования, и нужен он для понимания общей логики проведения таких манипуляций. В реальности тестируемые программы могут быть гораздо сложнее, а значит, и тестов тоже больше.