Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Манкипатчинг Python: Продвинутое тестирование

В предыдущем уроке мы тестировали гипотетическую функцию get_private_fork_names(org), применяя инверсию зависимостей. Вспомним содержимое этой функции в её исходном виде:

from github import Github

def get_private_fork_names(username):
    client = Github('access_token')
    # Клиент выполняет запрос на гитхаб и возвращает список
    # приватных репозиториев указанной организации
    repos = client.get_user(username).get_repos(type='private')
    # Оставляем только имена приватных форков
    return [repo.name for repo in repos if repo.fork == True]

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

# Подменяем get_repos так, чтобы не выполнялся сетевой запрос

# Где-то в другом файле
from github import Github, NamedUser

# NamedUser возвращается после вызова get_user()
# https://github.com/PyGithub/PyGithub/blob/master/github/NamedUser.py
def fake_get_repos():
  print('Nothing happened!')

# После выполнения этого кода, библиотека Github меняет свое поведение не только
# в этом модуле, но и вообще по всей программе
NamedUser.get_repos = fake_get_repos

client = Github()

# Вызывается подмененный get_repos
client.get_user().get_repos()
# => 'Nothing happened!'

Изменение модулей и классов прямо во время работы программы (в рантайме), называется манкипатчингом (monkey patching). Он считается опасной практикой при написании обычного кода в Python, но он очень популярен и удобен в библиотеках или во время тестирования.

Библиотека pook, типичный пример хорошего использования monkey patching в реальном программировании. С ее помощью подменяются запросы без прямого взаимодействия с HTTP-клиентом, как в случае инверсии зависимостей.

import pook
import requests # популярный HTTP-клиент

@pook.on
def test_my_api():
    # Патчим запрос на конкретную страницу и указываем что вернуть
    pook.get(
        'http://twitter.com/api/1/foobar',
        reply=404,
        response_json={'error': 'not found'}
    )

    # Сам запрос через библиотеку requests
    resp = requests.get('http://twitter.com/api/1/foobar')
    assert resp.status_code == 404
    assert resp.json() == {"error": "not found"}

Pook заменяет внутри библиотеки urllib3 метод urlopen(), который используется библиотеками поверх urllib3 для выполнения HTTP-запросов.

И пример использования:

@pook.on
def test_get_private_fork_names():
    pook.get(
        'https://api.github.com/orgs/hexlet/repos',
        reply=200,
        response_json=[{ 'fork': True, 'name': 'one' }, { 'fork': False, 'name': 'two' }]
    )

    names = get_private_fork_names('hexlet')
    assert names == # ожидаемый список имен

Вызов pook.get(url) задаёт полный адрес страницы, запрос к которой надо перехватить. Pook анализирует все выполняемые запросы и подменяет только тот, который соответствует данным параметрам.

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

В чём плюсы и минусы такого способа работы?

Главный плюс в том, что такой способ тестирования практически универсальный. Его можно использовать с любым кодом, без необходимости править сам код. Программа даже не будет догадываться о том, что её тестируют.

Минус же заключается в том, что тестирование "черным ящиком" превращается в тестирование "прозрачным ящиком". Это значит, что тест знает про устройство тестируемого кода и зависит от внутренностей. Такое знание делает тесты хрупкими. Функция может измениться без потери работоспособности, но тесты придётся переписывать, потому что они завязаны на конкретные значения домена, страниц и формата возвращаемых данных.


Дополнительные материалы

  1. Кассеты (VCR)
  2. Responses (Для Requests)

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
2 февраля 8 месяцев

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Изображение Тото

Задавайте вопросы, если хотите обсудить теорию или упражнения. Команда поддержки Хекслета и опытные участники сообщества помогут найти ответы и решить задачу