Тестирование PY кода c requests

Часто приходят заказы на разработку различного рода парсеров и средств автоматизирующих какую-то определенную деятельность на сайтах.

 

Например, один наш клиент, являющийся реселлером выделенных серверов пришел с проблемой. В одном из дата-центров, в котором он берет серверы, уже который год не могут сделать починить API для реселлеров. При этом весь необходимый функционал доступен через их сайт. Для нормального реселлера, у которого уже не 5 серверов это совершенно неудобно. Хочется, чтобы клиенты сами могли перезагрузить сервер, переставить ОС или закгрузить rescue mode/livecd без обращения в тех. поддержку реселлера. И реселлеру самому удобнее,  когда  просроченные сервера автоматически отключаются, чтобы клиенты сильно не откладывали процесс оплаты.

 

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

 

Так вот к чему я – любой нормальный проект должен быть покрыт тестами. Тем более тот, что связан в данном случае с парсингом данных, полученных в виде HTML ответа от веб-сервера. Проблема в том как это по-человечески тестировать.

 

Один из вариантов – работать на живой системе. Т.е. в каждом тесте каждая тестируемая функция будет делать реальный GET/POST запрос к странице, получать ответ, обрабатывать его и выдавать результат, который мы проверякс. Это плохо. Во-первых это медленно, а тесты должны работать насколько это возможно быстро, чтобы было желание их использовать. Во-вторых рано или поздно вас такого с радостью забанят, либо повесят ограничение на число запросов, в итоге результатом выполнения ваших тестов будет картина, вида :

 

OK OK OK FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL FAIL “.

 

Можно добавить задержку между тестами и тестировать несколько часов, но как-то это не очень.

 

Другой вариант – сохранить ответы с сайта в какие-нибудь файлы, в тестах подгружаем ответ из файла и делаем много monkeypatching. Например, сохраяем ответ GET-запроса http://somedomain/get/all/users в get_all_users.txt и далее (мы используем pytest):

Но это ручная работа утомительна. К тому же нужно следить за актуальностью сохраненных ответов. Если они уже не актуальны – руками удаляем, скачиваем заново и запускаем тесты повторно.

 

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

 

Как оказалось все уже решено до нас. Очень крутой товарищ Ian Cordasco (который выступал на PyCon, кстати), написал замечательную библиотеку Betamax. Которая написана под впечатлением библиотеки VCR из ruby-мира. Идея заключается в том, чтобы на лету патчить любые функции библиотеки (requests в данном случае) и автоматически сериализовывать HTTP-ответы. В дальнейшем, если ответ уже сериализован брать его с диска.

 

Вся та куча кода,  которая в свое время использовалась для подготовки теста свелась к одной небольшой фикстуре:

 

Теперь в тестируемый класс или функцию нужно подсунуть сессию, созданную в этой фикстире. Что особенно просто если мы любим dependency injection. После чего ответы генерируемые всеми запросами requests будут сериализовываться в папке fixtures в json формате в файл с названием теста.

 

Здесь при первом запуске метод get сделает реальный запрос к google.com, но при всех последующих запросах ответ будет браться с диска, в связи с чем тестирование в разы ускоряется и не надо боятся, что пока вы правите алоритм парсинга HTML-ответа с помощью BeutifulSoup и долбите сервер запросами, вас забанят.

 

Проблема того, что ответ HTTP-сервера, к которому вы делаете запрос, изменился, в связи с чем сохраненные на диске ответы уже невалидны, решаетается очень просто – удалите файлы из папки fixtures и запустите тесты заново. Betamax закэширует их заново.

 

Есть и ложка дегтя, конечно. Betamax не будет корректно работать, если вы делаете два абсолютно идентичных запроса, которые в реальности возвращают разный ответ и это влияет на логику тестируемых функций. Своего рода side-effect. В этом случае закэшируется один ответ и при последующих запусках оба запроса будут возвращать одинаковый ответ взятый из кэша. Такие случаи редки и лино я с этим пока не сталкивался, но проблема такая есть.

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *