Добавьте в файл conftest py обработчик который считывает из командной строки параметр language
Автоматизированное тестирование на базе pytest framework
Предисловие
Pytest - это среда автоматического тестирования, которая поставляется с python2. Если используется версия python3, среда pytest является независимой и требует установки pip
Выбор фреймворка pytest в качестве автоматизированного фреймворка является результатом сравнения нескольких фреймворков. Он имеет некоторые относительные преимущества.
По сравнению с фреймворком unittest несколько преимуществ:
1. Передняя функция приспособления
2. Отметка варианта использования, повторный запуск варианта использования и другие функции. Чтобы
3. Параметризация вариантов использования
4. Совместимость с unittest, носовым каркасом
Функция подсветки также является функцией прибора, которая будет представлена отдельно позже.
Официальное знакомство с преимуществами фреймворка pytest:
1. Очень легко использовать, легко начать, богатая документация, в документации есть много примеров для справки.
2. Возможность поддерживать простые модульные тесты и сложные функциональные тесты.
3. Поддержка параметризации
4. Процесс выполнения можно обвинить только в том, что он пропустил определенные тесты или пометил определенные ожидаемые случаи отказа как неудачные.
5. Поддержка повторных сбоев выполнения
6. Поддержка работы тестовых случаев, написанных носом и unittest.
7. Может создавать html-отчет
8. Удобная интеграция с инструментом непрерывной интеграции jenkins
9. Может поддерживать реализацию некоторых сценариев использования.
10. Имеет множество сторонних плагинов и может настраивать расширения.
Хороший кот - это хороший кот, который может ловить мышей, и хороший фреймворк, который может удовлетворить потребности большинства отделов, - хороший фреймворк. Затем, как использовать фреймворк для автоматизации тестирования, получите волну руководства
1. Один, скачать и установить
(1) Python2 поставляется с ним, pytest --version можно просмотреть напрямую или открыть через терминал: import pytest, ошибка не сообщается, то есть она есть, но python2 не будет поддерживаться после 2020 года, поэтому лучше как можно скорее перейти на среду python3
(2)python3,pip install -U pytest
(3) Просмотрите загруженный pip show pytest или pytest --version
(4) правила выполнения сценария использования pytest:
① Тестовый файл начинается с test_xx.py (или заканчивается _test)
② Тестовый класс начинается с Test и не может иметь метода инициализации
③ Тестовая функция или метод начинается с test_xx
Совет: вы можете назвать его только в соответствии с правилом именования. Если вы назовете файл самостоятельно: test_create_trans, он не будет распознан, только test_xxx.py
2. Разработка и выполнение тестового набора
Основные моменты
Создавайте качественные функции для других тестовых случаев
2.1 Введение в основные моменты:
1. функция крепления
Понять функцию фикстуры: аналогично методам установки и разрыва в фреймворке unittest, нет необходимости отображать вызов, фреймворк выполнится автоматически, но фикстур отличается
①Он может отображать вызов или установить автоматический вызов
②Display вызов, прибор передается напрямую как параметры других тестовых примеров
③Определите объем выполнения непосредственно через область действия параметра scope
④Название функции для пользовательской предварительной операции, нефиксированной настройки, разборки и т. Д.
Синтаксис фикстуры:
Инструкции по использованию:
@ pytest.fixture - это декоратор pytest. Те, кто знаком с синтаксисом python, знают декоратор. Если функцию необходимо использовать как функцию фиксации, зарегистрируйте ее как фикстуру перед функцией. Подробнее см. в примере кода приложения.
Реализованные функции:
Некоторые общие методы, которые необходимо выполнить перед выполнением тестового примера, могут быть выполнены заранее, и выполнение будет определяться в соответствии с установленным уровнем сеанса:
Приложения:В качестве примера возьмем вход в систему: поскольку в основном все функциональные тесты требуют входа в систему, она используется как функция перед выполнением, как показано ниже:
Конкретные результаты выполнения показаны на рисунке ниже.
2.Функция параметризации вариантов использования
грамматика
@ pytest.mark.paramertrize ("имя параметра", список, индекс),
Инструкции по использованию
Реализованные функции:
Может реализовать параметризацию тестовых случаев и комбинацию параметров, подробности см. В примерах приложений.
Приложения:
Результаты:
3. Функция маркировки вариантов использования
Синтаксис: @ pytest.mark.xxx
Инструкция по применению: @ pytest.mark.xxxxxx - это имя тега, определенное лично. Например, если определенный вариант использования интерфейса находится на веб-стороне, а определенный вариант использования интерфейса - на стороне приложения, тогда можно выделить метки, например: @ pytest.mark.web; @ pytest.mark.app
Реализованные функции:Пример использования отличительного знака, Подключиться,Если вам нужно протестировать только веб-сторону, вы можете указать вариант использования, который необходимо выполнить, с помощью параметра -m в командной строке:
То есть $ pytest -m web test_01.py
Тогда pytest будет выполнять только веб-тестовые примеры. Конкретные результаты см. В разделе примеров приложений.
Приложения
Результаты
4. conftest файл:
Файл conftest - это файл конфигурации, который по умолчанию читается фреймворком pytest, поэтому вам нужно обратить внимание на:
①Имя конфигурационного скрипта conftest.py фиксировано и не может быть изменено
②conftest.py и запущенный тестовый пример должны быть в одном пакете, а __init__.py
③ Нет необходимости импортировать conftest.py, варианты использования pytest будут найдены автоматически
④ Если вы хотите реализовать фикстуры уровня сеанса, они должны быть помещены в этот файл. Если новый файл инкапсулирует фикстуры, вызовы уровня сеанса не могут быть реализованы
2.2 Знакомство с другими высококачественными функциями
Остальные функции просты в использовании и понятны.
1.Ошибка тега варианта использования:pytest.xfail(msg)
Это означает: в каком случае условие не выполняется, вы ожидаете, что оно завершится неудачно, и помечаете тестовый пример как неудачный, msg - это строка
2.Выход из варианта использования:pytest.exit(msg)
Значение: во время выполнения тестового примера есть некоторые методы, которые необходимо передать, прежде чем их можно будет использовать для других методов, таких как вход в систему. Если вход в систему не удается, то нет необходимости выполнять другие методы, просто отметьте выход напрямую.
3. Пропуск варианта использования: существует три типа:
Для конкретного использования вышеуказанного, пожалуйста, обратитесь к приложенному коду
Файл тестового примера: test_truck.py
2.3 Выполнение тестового примера
В процессе написания тестовых случаев вы можете выполнять их во время написания. Есть два способа выполнить их:
①Режим командной строки:
pytest [options] [file]
Какие конкретные параметры можно просмотреть с помощью $ pytest --help
Обычно используются -s, -m, -q
②pycharm исполнение
Необходимо выполнить в режиме py.test, конкретные шаги настройки Pycharm: File-Settings-Tools-Python Integrated Tools, подробности см. На следующем рисунке:
Три, генерация отчета об испытаниях
Второй шаг - выполнить вариант использования. После завершения, будь то pycharm или командная строка, он запишет результат теста, но он недостаточно интуитивно понятен, поэтому используйте плагин для более интуитивного просмотра отчета о тестировании.
3.1. Отчет о тестировании простой версии: pytest-html
(1) установка pytest-html
pip install pytest-html
pip show pytest-html
(2) pytest-html создает отчет о тестировании
① Перейдите в каталог файла, который нужно запустить
②Выполнение назначенных вариантов использования
pytest test_exception.py --html=../reports/test_exception.html
(3) Повторный запуск теста
$ pip install pytest-rerunfailures
$ pip show pytest-rerunfailures
pytest --reruns 1 test_exception.py --html=../reports/test_exception.html
Стиль страницы отчета следующий:
3.2.Pytest + Allure генерирует отчеты о тестировании высокого уровня
Необходимо подготовить:
pycharm: поскольку html-отчет, созданный allure, не может быть открыт, он может отображаться нормально при открытии pycharm
pytest-allure-adapter: специальная функция: для создания отчета о тестировании xml
Специальная функция allure2.7.0: инструмент allure генерирует html-отчет из xml
java1.8: зависимая среда Allure
1. Загрузите pytest-allure-adapter
pip install pytest-allure-adaptor
pip show pytest-allure-adaptor
2. Попробуйте сгенерировать xml-файл отчета о тестировании через pytest-allure-adapter
$pytest -s -q test_trans.py --alluredir report
3. Скачайте allure,
Загрузите последнюю версию allure2, после загрузки и установки добавьте «E: \ allure-2.7.0 \ bin» в переменную среды. Чтобы использовать команду позже, как и другое программное обеспечение, использующее метод командной строки, добавьте переменную среды, обычно после добавления После этого необходимо перезагрузить компьютер
4. Сгенерируйте html-отчет из xml-файла с помощью команды allure.
allure сгенерирует тестовый каталог XML-файла, созданный allure / -o каталог хранения тестового отчета /
Например: allure generate --clean allure_xml / 2019-02-21 -o allure_html / 2019-02-21
5. Откройте отчет о тестировании через pycharm.
Потому что откройте исключение отображения напрямую, поэтому выполните следующие шаги, чтобы следующим способом просмотреть
Я пытаюсь создать структуру, в которой пользователь дает значение параметра командной строки при запуске pytest, и внутренне я использую это значение внутри файла conftest.py, чтобы решить, с какими данными должны выполняться тесты.
Мой файл conftest.py находится в корневой папке теста. Я пытаюсь извлечь значение командной строки, заданное пользователем, и передать его в приборы, которые, в свою очередь, используются тестами.
Проблема в том, что я не вижу значения, которое пользователь передает через командную строку, используемую в файле conftest.py, поскольку переменная testType все еще имеет пустое строковое значение. Любая помощь очень ценится.
2 ответа
У меня есть проект, который имеет следующую структуру: Project/ | +-- src/ | | | +-- proj/ | | | +-- __init__.py | +-- code.py | +-- tests/ | | | +-- __init__.py | +-- conftest.py | +-- test_code.py +-- doc/ +-- pytest.ini +-- setup.py Импортируемый пакет proj находится в каталоге src . Тесты.
Попытка взять аргумент командной строки (table_name) в pytest (через conftest.py, см. ниже) и использовать этот аргумент в вспомогательном методе для создания запроса в DB, а затем использовать результаты запроса для создания параметризованных тестовых входных данных с помощью.
Я использовал плагин simple-settings для передачи значений в светильниках через командную строку. Поскольку pytest_generate_tests не смог передать значения, если светильники записаны внутри файла conftest.py.
Похожие вопросы:
Я написал модульные тесты pytest для пакета, который я написал со структурой, подобной приведенной ниже: packagedir setup.py мой пакет __init__.py functionpackage __init__.py functionmodule.py тест.
в pytest framework conftest.py позволяет настроить тестовую среду до начала выполнения любого теста. Например, чтение аргументов командной строки, настройка UUT, настройка регистратора и т. д. Как.
Короче говоря, я хочу иметь возможность пропустить некоторые тесты, Если сеанс выполняется против нашего производственного API. Среда, в которой выполняются тесты, устанавливается с помощью.
Вернуться Дальше
В этой главе мы рассмотрим файлы конфигурации, которые влияют на pytest, обсудим, как pytest изменяет свое поведение на их основе, и внесем некоторые изменения в файлы конфигурации проекта Tasks.
Примеры в этой книге написаны с использованием Python 3.6 и pytest 3.2. pytest 3.2 поддерживает Python 2.6, 2.7 и Python 3.3+.
Под спойлером приведен список статей этой серии.
Конфигурация
До сих пор в этой книге я говорил о различных нетестовых файлах, которые влияют на pytest в основном мимоходом, за исключением conftest.py, который я довольно подробно рассмотрел в главе 5, Плагины, на странице 95. В этой главе мы рассмотрим файлы конфигурации, которые влияют на pytest, обсудим, как pytest изменяет свое поведение на их основе, и внесем некоторые изменения в файлы конфигурации проекта Tasks.
Понимание файлов конфигурации pytest
Прежде чем я расскажу, как вы можете изменить поведение по умолчанию в pytest, давайте пробежимся по всем не тестовым файлам в pytest и, в частности, кто должен заботиться о них.
Следует знать следующее:
- pytest.ini: Это основной файл конфигурации Pytest, который позволяет вам изменить поведение по умолчанию. Поскольку вы можете внести довольно много изменений в конфигурацию, большая часть этой главы посвящена настройкам, которые вы можете сделать в pytest.ini .
- conftest.py: Это локальный плагин, позволяющий подключать хук-функции и фикстуры для каталога, в котором существует файл conftest.py , и всех его подкаталогов. Файл conftest.py описан в главе 5 «Плагины» на стр. 95.
- __init__.py : При помещении в каждый test-подкаталог этот файл позволяет вам иметь идентичные имена test-файлов в нескольких каталогах test. Мы рассмотрим пример того, что пойдет не так без файлов __init__.py в тестовых каталогах в статье «Избегание коллизий имен файлов» на стр. 120.
Если вы используете tox, вас заинтересует:
- tox.ini: Этот файл похож на pytest.ini , но для tox . Однако вы можете разместить здесь свою конфигурацию pytest вместо того, чтобы иметь и файл tox.ini , и файл pytest.ini , сохраняя вам один файл конфигурации. Tox рассматривается в главе 7, "Использование pytest с другими инструментами", на стр. 125.
Если вы хотите распространять пакет Python (например, Tasks), этот файл будет интересен:
- setup.cfg: Это также файл в формате INI, который влияет на поведение файла setup.py . Можно добавить несколько строк в setup.py для запуска python setup.py test и запустить все ваши тесты pytest. Если вы распространяете пакет, возможно, у вас уже есть файл setup.cfg , и вы можете использовать этот файл для хранения конфигурации Pytest. Вы увидите, как это делается в Приложении 4, «Упаковка и распространение проектов Python», на стр. 175.
Независимо от того, в какой файл вы поместили конфигурацию pytest, формат будет в основном одинаковым.
Единственное отличие состоит в том, что заголовок раздела для setup.cfg — это [tool:pytest] вместо [pytest] .
List the Valid ini-file Options with pytest –help
Вы можете получить список всех допустимых параметров для pytest.ini из pytest --help :
Вы увидите все эти настройки в этой главе, за исключением doctest_optionflags , который рассматривается в главе 7, "Использование pytest с другими инструментами", на странице 125.
Плагины могут добавлять опции ini-файлов
Предыдущий список настроек не является константой. Для плагинов (и файлов conftest.py) возможно добавить опции файла ini. Добавленные опции также будут добавлены в вывод команды pytest --help.
Теперь давайте рассмотрим некоторые изменения конфигурации, которые мы можем внести с помощью встроенных настроек INI-файла, доступных в core pytest.
Изменение параметров командной строки по умолчанию
Вы использовали уже некоторые параметры командной строки для pytest, таких как -v/--verbose для подробного вывода -l/--showlocals для просмотра локальных переменных с трассировкой стека для неудачных тестов. Вы можете обнаружить, что всегда используете некоторые из этих options—or и предпочитаете использовать them—for a project . Если вы устанавливаете addopts в pytest.ini для нужных вам параметров, то вам больше не придется вводить их. Вот набор, который мне нравится:
Зарегистрировав эти маркеры, вы теперь также можете увидеть их с помощью pytest --markers с их описаниями:
If you use markers in pytest.ini to register your markers, you may as well add --strict to your addopts while you’re at it. You’ll thank me later. Let’s go ahead and add a pytest.ini file to the tasks project:
Если вы используете маркеры в pytest.ini для регистрации своих маркеров, вы также можете добавить --strict к своим addopts . Ты поблагодаришь меня позже. Давайте продолжим и добавим файл pytest.ini в проект задач:
Если вы используете маркеры в pytest.ini для регистрации маркеров, вы можете также добавить --strict к имеющимся при помощи addopts . Круто?! Отложим благодарности и добавим файл pytest.ini в проект tasks :
Здесь комбинация флагов предпочитаемые по умолчанию:
- -rsxX , чтобы сообщить, какие тесты skipped, xfailed, или xpassed,
- --tb = short для более короткой трассировки при сбоях,
- --strict что бы разрешить только объявленные маркеры.
И список маркеров для проекта.
Это должно позволить нам проводить тесты, в том числе дымовые(smoke tests):
Требование минимальной версии Pytest
Параметр minversion позволяет указать минимальную версию pytest, ожидаемую для тестов. Например, я задумал использовать approx() при тестировании чисел с плавающей запятой для определения “достаточно близкого” равенства в тестах. Но эта функция не была введена в pytest до версии 3.0. Чтобы избежать путаницы, я добавляю следующее в проекты, которые используют approx() :
Остановка pytest от поиска в неправильных местах
Знаете ли вы, что одно из определений «recurse» заключается в том, что бы дважды выругаться в собственом коде? Ну, нет. На самом деле, это означает учет подкаталогов. pytest включит обнаружение тестов рекурсивно исследуя кучу каталогов. Но есть некоторые каталоги, которые вы хотите исключить из просмотра pytest.
Значением по умолчанию для norecurse является '. * Build dist CVS _darcs and *.egg. Having '.*' — это хорошая причина назвать вашу виртуальную среду '.venv', потому что все каталоги, начинающиеся с точки, не будут видны.
В случае проекта Tasks, не помешает указать src , потому что поиск в тестовых файлах с помощью pytest будет пустой тратой времени.
При переопределении параметра, который уже имеет полезное значение, такого как этот параметр, полезно знать, какие есть значения по умолчанию, и вернуть те, которые вам нужны, как я делал в предыдущем коде с *.egg dist build .
norecursedirs — своего рода следствие для тестовых путей, поэтому давайте посмотрим на это позже.
спецификация дерева тестового каталога
В то время как norecursedirs указывает pytest куда не надо заглядыывать, testpaths говорит pytest, где искать. testspaths — это список каталогов относительно корневого каталога для поиска тестов. Он используется только в том случае, если в качестве аргумента не указан каталог, файл или nodeid .
Предположим, что для проекта Tasks мы поместили pytest.ini в каталог tasks_proj вместо тестов:
Тогда может иметь смысл поместить тесты в testpaths :
Теперь, если вы запускаете pytest из каталога tasks_proj , pytest будет искать только в tasks_proj/tests . Проблема здесь в том, что во время разработки и отладки тестов я часто перебираю тестовый каталог, поэтому я могу легко тестировать подкаталог или файл, не указывая весь путь. Поэтому мне этот параметр мало помогает в интерактивном тестировании.
Тем не менее, он отлично подходит для тестов, запускаемых с сервера непрерывной интеграции или с tox-а. В этих случаях вы знаете, что корневой каталог будет фиксированным, и вы можете перечислить каталоги относительно этого фиксированного корневого каталога. Это также те случаи, когда вы действительно хотите сократить время тестирования, так что избавиться от поиска тестов — это здорово.
На первый взгляд может показаться глупым использовать одновременно и тестовые пути, и norecursedirs . Однако, как вы уже видели, тестовые пути мало помогают в интерактивном тестировании из разных частей файловой системы. В этих случаях norecursedirs могут помочь. Кроме того, если у вас есть каталоги с тестами, которые не содержат тестов, вы можете использовать norecursedirs , чтобы избежать их. Но на самом деле, какой смысл ставить дополнительные каталоги в тесты, которые не имеют тестов?
Изменение Правил Обнаружения Тестов
pytest находит тесты для запуска на основе определенных правил обнаружения тестов. Стандартные правила обнаружения тестов:
• Начните с одного или нескольких каталогов. Вы можете указать имена файлов или каталогов в командной строке. Если вы ничего не указали, используется текущий каталог.
• Искать в каталоге и во всех его подкаталогах тестовые модули.
• Тестовый модуль — это файл с именем, похожим на test_*.py или *_test.py .
• Посмотрите в тестовых модулях функции, которые начинаются с test.
• Ищите классы, которые начинаются с Test. Ищите методы в тех классах, которые начинаются с `test , но не имеют метода init`.
Это стандартные правила обнаружения; Однако вы можете изменить их.
python_classes
Обычное правило обнаружения тестов для pytest и классов — считать класс потенциальным тестовым классом, если он начинается с Test* . Класс также не может иметь метод __init__() . Но что, если мы захотим назвать наши тестовые классы как <something>Test или <something>Suite ? Вот где приходит python_classes :
Это позволяет нам называть классы так:
python_files
Как и pytest_classes , python_files изменяет правило обнаружения тестов по умолчанию, которое заключается в поиске файлов, начинающихся с test_* или имеющих в конце *_test .
Допустим, у вас есть пользовательский тестовый фреймворк, в котором вы назвали все свои тестовые файлы check_<something>.py . Кажется разумным. Вместо того, чтобы переименовывать все ваши файлы, просто добавьте строку в pytest.ini следующим образом:
Очень просто. Теперь вы можете постепенно перенести соглашение об именах, если хотите, или просто оставить его как check_* .
python_functions
python_functions действует как две предыдущие настройки, но для тестовых функций и имен методов. Значение по умолчанию — test_* . А чтобы добавить check_* —вы угадали—сделайте это:
Соглашения об именах pytest не кажутся такими уж ограничивающими, не так ли? Так что, если вам не нравится соглашение об именах по умолчанию, просто измените его. Тем не менее, я призываю вас иметь более вескую причину для таких решений. Миграция сотен тестовых файлов — определенно веская причина.
Запрет XPASS
Установка xfail_strict = true приводит к тому, что тесты, помеченные @pytest.mark.xfail , не распознаются, как вызвавшие ошибку. Я думаю, что эта установка должно быть всегда. Дополнительные сведения о маркере xfail см. В разделе "Маркировка тестов ожидающих сбоя" на стр. 37.
Предотвращение конфликтов имен файлов
Полезность наличия файла __init__.py в каждом тестовом подкаталоге проекта долго меня смущали. Однако разница между тем, чтобы иметь их или не иметь, проста. Если у вас есть файлы __init__.py во всех ваших тестовых подкаталогах, вы можете иметь одно и то же тестовое имя файла в нескольких каталогах. А если нет, то так сделать не получится.
Вот пример. Каталог a и b оба имеют файл test_foo.py . Неважно, что эти файлы содержат в себе, но для этого примера они выглядят так:
ch6/dups/b/test_foo.py
С такой структурой каталогов:
Эти файлы даже не имеют того же контента, но тесты испорчены. Запускать их по отдельности получится, а запустить pytest из каталога dups нет:
Теперь давайте попробуем еще раз с верхнего уровня в dups_fixed :
Так то будет лучше.
Вы, конечно, можете убеждать себя, что у вас никогда не будет повторяющихся имен файлов, поэтому это не имеет значения. Все, типа, нормально. Но проекты растут и тестовые каталоги растут, и вы точно хотите дождаться, когда это случиться с вами, прежде чем позаботиться об этом? Я говорю, просто положите эти файлы туда. Сделайте это привычкой и не беспокойтесь об этом снова.
Упражнения
In Chapter 5, Plugins, on page 95, you created a plugin called pytest-nice that included a --nice command-line option. Let’s extend that to include a pytest.ini option called nice.
В главе 5 «Плагины» на стр. 95 вы создали плагин с именем pytest-nice который включает параметр командной строки --nice . Давайте расширим это, включив опцию pytest.ini под названием nice .
- Добавьте следующую строку в хук-функцию pytest_addoption pytest_nice.py : parser.addini('nice', type='bool', help='Turn failures into opportunities.')
- Места в плагине, которые используют getoption() , также должны будут вызывать getini('nice') . Сделайте эти изменения.
- Проверьте это вручную, добавив nice в файл pytest.ini .
- Не забудьте про тесты плагинов. Добавьте тест, чтобы убедиться, что параметр nice из pytest.ini работает корректно.
- Добавьте тесты в каталог плагинов. Вам нужно найти некоторые дополнительные функции Pytester.
Что дальше
В то время как pytest является чрезвычайно мощным сам по себе—особенно с плагинами—он также хорошо интегрируется с другими инструментами разработки программного обеспечения и тестирования программного обеспечения. В следующей главе мы рассмотрим использование pytest в сочетании с другими мощными инструментами тестирования.
Вторая часть выступления Рафаэля Пирцины (Raphael Pierzina), которым открылся PyBerlin 2019. Презентуются возможности Pytest для организации автоматизированного тестирования проекта на Python, в том числе использование специальных зафиксированных объектов (fixtures), исключение из прогона медленных тестов и написание двух плагинов (pytest plugin). Полный перевод текста выступления с примерами тестовых комплексов см. далее.
- Добавление индивидуальной метки
- Новый тест, объединяющий все сценарии
- Плагин для исключения медленных тестов по умолчанию
- Тесты с вовлечением нескольких мероприятий
- Использование метки с конкретными параметрами зафиксированного объекта
- Добавление файла переменных в конфигурацию pytest
- Плагин для кэширования времени выполнения тестов
- Обновление точки добавления кода (hook) для исключения тестов
- Плагин для запуска тестов со специальным зафиксированным объектом (fixture)
Всем привет! добро пожаловать на вторую часть данной инструкции по pytest. В первой части мы расширили покрытие кода (code coverage) в проекте earth с 53% до 98%. Для этого мы разработали серию автоматизированных тестов на основе pytest и использовали пример из файла README проекта.
Однако, еще много чего можно улучшить, особенно с точки зрения продуктивности разработчика, потому что прогон новых тестов занимает больше 20 секунд. Причиной этого является то, что test_large_group использует все функции из модуля adventurers , и, когда pandas готовятся к мероприятию, им нужно поесть, а это требует времени!
Кроме того, три новых теста почти идентичны. Различаются только их параметры (parameters) и метки (markers). Это будет хорошим примером для реализации функций параметризации pytest.
Как и в предыдущей части, можно отслеживать изменения кода, которые мы будем вносить по мере продвижения по данной инструкции, с помощью коммитов в ветке write-pytest-plugins репозитория earth .
См. далее тесты и зафиксированные объекты (fixtures) из предыдущей части:
Добавление индивидуальной метки
Начнем данную инструкцию с добавления индивидуальной метки к тестам, в которых задействован объект TXL airport . Позднее мы будем использовать ее в качестве алиаса для метки xfail , а также создадим для нее документацию.
Новый тест, объединяющий все сценарии
Все вышеприведенные тесты зависят от разных групп искателей приключений:
- small_group
- large_group
- no_pandas_group
Самый быстрый способ переключаться между разными группами по принципу отдельного элемента теста — это создать зафиксированный объект (fixture), который возвращает конкретную группу для заданного параметра.
Мы сможем генерировать несколько тестовых элементов из одной тестовой функции с помощью параметризации (parametrize). Для этого нам понадобится функция pytest.param, которой мы передадим метки для именованных аргументов (keyword argument). Таким образом мы добавим метки к конкретным параметрам тестов.
Обратите внимание на то, что мы также добавим метки, которые будут применяться ко всем тестовым элементам, к тестовой функции.
Удаление тестов, которые стали избыточными. Поскольку мы создали новый тест, который объединяет все три разных сценария, мы можем удалить первоначальные тесты. Обязательно проверьте, чтобы покрытие кода осталось на уровне 98%, и мы не пропустили ни одного сценария.
Плагин для исключения медленных тестов по умолчанию
Давайте создадим файл conftest.py и добавим специальную опцию для командной строки. После этого мы модифицируем коллекцию тестовых элементов (test items collection) и автоматически исключим тесты с медленной меткой:
Теперь pytest по умолчанию исключает медленные тесты:
Но если мы захотим прогнать все тесты, можно добавить специальную опцию для командной строки:
Тесты с вовлечением нескольких мероприятий
На данный момент мы прогоняем свои тесты по одному мероприятию, которое проводится в мае на территории Северной Америки:
Это необоснованный риск. Использование библиотеки может закончиться аварийным отказом, если ее использовать по различным сочетаниям местоположений и месяцев проведения мероприятий! Покрытие кода — хороший показатель того, какие строки исходного кода выполняются во время прогона тестов, однако оно не может измерить качество ваших тестовых сценариев.
Было бы разумно прогнать наши тесты, сделанные по методу счастливого пути (happy path tests), по нескольким возможным сценариям. Мы могли бы использовать информацию о реальных конференциях Python, чтобы сгенерировать больше мероприятий для наших тестов. Великолепный плагин pytest-variables загружает тестовые данные из файла JSON и предоставляет доступ к этим данным через зафиксированный объект (fixture) variables .
Давайте создадим новый файл JSON:
Теперь мы модифицируем зафиксированный объект event , чтобы в нем появились дополнительные параметры, а также создадим экземпляры класса (instances) Event на основе информации, полученной из загруженного файла JSON:
Использование метки с конкретными параметрами зафиксированного объекта
Однако, есть один пограничный случай (edge case), который не учитывается в нынешней реализации зафиксированного объекта. Нам нужно добавить специальную метку xfail к мероприятиям, которые проводятся в Европе, потому что посетителям придется приземлиться в TXL airport , чтобы добраться до мероприятия.
Теперь, во время прогона тестов нам нужно будет указать файл, из которого будут загружаться переменные (variables):
Добавление файла переменных в конфигурацию pytest
Вместо того, чтобы каждый раз запускать pytest вручную с этой опцией командной строки, лучше указать опции, которые будут добавляться к нашей конфигурации pytest по умолчанию:
Плагин для кэширования времени выполнения тестов
Предыдущую часть данной инструкции я закончил словами о том, что мы сделаем что-нибудь по-настоящему крутое с помощью pytest. У нас нет никакой возможности узнать, сколько времени займет тест, до его запуска. Однако, мы, все же, знаем, сколько времени он занял в прошлый прогон. Мы можем отслеживать данную информацию на следующем запуске тестов.
Что должен делать наш плагин:
- отслеживать время работы теста в точке добавления кода (hook) pytest_runtest_logreport
- записывать данные о времени работы теста в кэш через точку добавления кода pytest_sessionfinish .
Кроме того, нам нужно, чтобы плагин:
- попытался загрузить данные о времени работы теста из кэша при запуске
- добавил метку (marker) для медленных тестов в точке добавления кода pytest_collection_modifyitems
Мы можем зарегистрировать локальные плагины в файле conftest.py :
Обновление точки добавления кода (hook) для исключения тестов
Теперь нам нужно обновить свою реализацию точки добавления кода pytest_collection_modifyitems , чтобы дополнительно исключать тесты с помощью новой метки turtle .
Плагин для запуска тестов со специальным зафиксированным объектом (fixture)
При работе с зафиксированными объектами нередко прогоняют тесты, чтобы проверить, что зафиксированные объекты работают как ожидается, меняют код зафиксированного объекта и запускают тесты еще раз. Для таких ситуаций есть смысл запускать только те тесты, которые используют новый зафиксированный объект, и исключить все остальные тесты.
Давайте напишем для этого плагин!
Прекрасно, вы добрались до конца этой части!
Мы посмотрели, когда и как запускать тесты с различными тестовыми параметрами, как исключить отдельные тесты, как загрузить тестовые данные из файла JSON, как использовать кэш pytest для хранения и загрузки времени работы теста и, наконец, как запускать тесты, которые используют определенный зафиксированный объект.
Вернуться Дальше
Достаточно мощный pytest прямо из коробки, становится еще лучше, когда вы добавляете в него микс из плагинов. Кодовая база pytest структурирована настройками и расширениями, и есть хуки, доступные для модификации и улучшений через плагины.
Примеры в этой книге написаны с использованием Python 3.6 и pytest 3.2. pytest 3.2 поддерживает Python 2.6, 2.7 и Python 3.3+.
Под спойлером приведен список статей этой серии.
Поехали дальше!
Возможно вы удивитесь узнав, что вы уже написали какие то плагины, если вы проработали предыдущие главы в этой книге. Каждый раз, когда вы помещаете фикстуры и/или hook-функции в файл conftest.py верхнего уровня проекта, вы создаёте локальный плагин conftest . Это просто небольшая дополнительная работа по преобразованию этих файлов conftest.py в устанавливаемые плагины, которые вы можете разделить между проектами, с другими людьми или с миром.
В этой главе вы узнаете, как создавать плагины, и я укажу вам правильное направление для их тестирования, упаковки и распространения. Полная тема упаковки и распространения Python, слишком обширна и претендует на собственную книгу, поэтому мы не будем охватывать все. Но вы получите достаточно сведений, чтобы иметь возможность обмениваться плагинами с вашей командой. Я также расскажу о некоторых простых способах для создания плагинов с поддержкой PyPI и наименьшим количеством работы.
Поиск плагинов
Вы можете найти сторонние плагины pytest в нескольких местах. Плагины, перечисленные в Приложении 3, Plugin Sampler Pack, на стр. 163, доступны для загрузки с PyPI. Тем не менее, это не единственное место для поиска отличных плагинов pytest.
На главном сайте документации pytest есть страница, в которой рассказывается об установке и использовании плагинов pytest и перечислены несколько распространенных плагинов.
Установка плагинов
Плагины pytest устанавливаются с pip, как и другие пакеты Python. Однако,
вы можете использовать pip несколькими способами для установки плагинов.
Установка из PyPI
Поскольку PyPI является местоположением по умолчанию для pip, установка плагинов из PyPI является самым простым методом. Давайте установим плагин pytest-cov :
Будет установлена последняя стабильная версия от PyPI.
Установка определенной версии из PyPI
Если вы хотите конкретную версию плагина, вы можете указать версию после == :
Установка из файла .tar.gz или .whl
Пакеты на PyPI распространяются как zip-файлы с расширениями .tar.gz и/или .whl . Они часто упоминаются как «tar balls» и «wheels». Если у вас возникли проблемы с попыткой работать с PyPI напрямую (что может случиться с брандмауэрами и другими сетевыми осложнениями), вы можете загрузить либо .tar.gz , либо .whl и установить из этого-того.
Вам не нужно распаковывать или танцевать с бубном; просто укажите pip на него:
Установка из локального каталога
Вы можете иметь заначку плагинов (и других пакетов Python) в локальном или общем каталоге в формате .tar.gz или .whl и использовать это вместо PyPI для установки плагинов:
--no-index указывает pip не подключаться к PyPI. --find-links=./some_plugins/ указывает pip искать в каталоге some_plugins . Этот метод особенно полезен, если у вас есть как сторонние, так и собственные плагины, хранящиеся локально, а также если вы создаете новые виртуальные среды для непрерывной интеграции или с tox. (Мы поговорим как о tox, так и о непрерывной интеграции в главе 7, используя pytest с другими инструментами, на странице 125.)
Обратите внимание, что с помощью метода установки локального каталога вы можете установить несколько версий и указать, какую версию вы хотите, добавив == и номер версии:
Установка из репозитория Git
Вы можете установить плагины непосредственно из Git-репозитория в этом случае GitHub:
Можно также указать тег версии:
Или можно указать ветвь:
Установка из репозитория Git особенно полезна, если вы храните свою собственную работу в Git или если требуемая версия плагина или плагин отсутствует в PyPI.
pip поддерживает установку из Git, Mercurial, Subversion и Bazaar и определяет тип VCS, используя префиксы url: «git+», «hg+», «svn+», «bzr+».
Более подробно можно ознакомиться в документации PyPI
Написание собственных плагинов
Чтобы сохранить изменения поведения отдельно от обсуждения механики плагинов, мы внесем изменения в conftest.py до того, как превратим его в распространяемый плагин. Вам не нужно запускать плагины таким образом. Но часто изменения, которые вы намеревались использовать только в одном проекте, станут достаточно полезными, чтобы поделиться ими и превратиться в плагин. Поэтому мы начнем с добавления функциональности в файл conftest.py, а затем, после того, как все заработает в conftest.py, переместим код в пакет.
Вернемся к проекту Tasks. В разделе "ожидание исключений" на странице 30 мы написали несколько тестов, которые проверяли, вызываются ли исключения, если кто-то неправильно вызвал функцию API. Похоже, мы пропустили хотя бы несколько возможных состояний ошибки.
Вот еще пара тестов:
ch5/a/tasks_proj/tests/func/test_api_exceptions.py
Давайте запустим их, чтобы проверить, проходят ли они:
Давайте запустим его снова с -v для подробностей. Поскольку вы уже видели трассировку, вы можете отключить ее, нажав --tb=no .
Затем мы изменим отчет о состоянии теста, чтобы изменить F на O и FAILED на OPPORTUNITY for improvement . Есть хук, который позволяет эту интрижку: pytest_report_teststatus() :
И теперь мы имеем как раз выход мы искали. Тестовый сеанс без флага --verbose показывает O для сбоев, то есть, возможности улучшения:
С флагом -v или --verbose будет получше:
Стоит заметить, что для этого плагина мы используем только пару хуков. Есть много других, которые можно найти на главном сайте документации Pytest.
Теперь мы можем вручную протестировать наш плагин, просто запустив его в нашем примере. Во-первых, без опции --nice , чтобы убедиться, что отображается только имя пользователя:
Теперь с --nice и --verbose :
Отлично! Все изменения, которые мы хотели сделать, сделаны примерно в десятке строк кода файла conftest.py . Далее мы переместим этот код в структуру плагина.
Создание устанавливаемого плагина
Процесс обмена плагинами с другими пользователями четко определен. Даже если вы никогда не включите свой собственный плагин в PyPI, то пройдя через этот процесс, вам будет легче читать код из плагинов с открытым исходным кодом, и у вас будет больше возможностей оценить, помогут они вам или нет.
Было бы излишним полностью охватывать packaging и distribution пакетов Python в этой книге, так как эта тема хорошо документирована в другом месте. Тут и здесь и еще здесь на русском. Тем не менее, перейти от локального подключаемого модуля конфигурации, который мы создали в предыдущем разделе, к чему-то устанавливаемому с помощью pip, является несложной задачей. ,
Во-первых, нам нужно создать новый каталог для размещения нашего кода плагина. Неважно, как вы это называете, но, поскольку мы создаем плагин для флага «nice», давайте назовем его «pytest-nice». У нас будет два файла в этом новом каталоге: pytest_nice.py и setup.py. (Каталог тестов будет обсуждаться в разделе «Плагины тестирования» на странице.105.)
В pytest_nice.py , мы поместим точное содержимое нашего conftest.py, которое было связано с этой функцией (и извлечем его из tasks_proj/tests/conftest.py ):
ch5/pytest-nice/pytest_nice.py
В setup.py нам нужен максимальноминимальный вызов setup() :
Вам понадобится больше информации в настройках, если вы собираетесь распространять ее среди широкой аудитории или в интернете. Однако для небольшой команды или просто для себя этого будет достаточно.
Вы можете включить еще какие то параметры для setup() ; а тут у нас только обязательные поля. Поле версии является версией этого плагина. И это целиком зависит от вас, когда вы поднимаете версию. Поле URL обязательно для заполнения. Вы можете оставить его пустым, но вы получите предупреждение. Поля author и author_email можно заменить на maintainer и maintainer_email , но одна из этих пар должна быть там. Поле license -лицензия представляет собой короткое текстовое поле. Это может быть одна из многих лицензий с открытым исходным кодом, ваше имя или компании, или что-то подходящее для вас. Запись py_modules перечисляет pytest_nice как наш единственный модуль для этого плагина. Хотя это список, и вы можете включить более одного модуля, если бы у меня было больше одного, я бы использовал пакет и поместил все модули в один каталог.
До сих пор все параметры setup() являются стандартными и используются для всех инсталляторов Python. Частью, которая отличается для плагинов Pytest, является параметр entry_points . Мы перечислили entry_points=,. Функция entry_points является стандартной для setuptools , но pytest11 специальный идентификатор, который ищет pytest. В этой строке мы сообщаем pytest, что nice -это имя нашего плагина, а pytest_nice -имя модуля, в котором живет наш плагин. Если бы мы использовали пакет, наша запись здесь была бы:
Я еще не говорил о файле README.rst . Некоторая форма README является требованием setuptools. Если вы пропустите его, вы получите это:
Сохранение README в качестве стандартного способа включения некоторой информации о проекте-хорошая идея в любом случае. Вот что я положил в файл для pytest-nice:
Есть много мнений о том, что должно быть в файле README. Это сильно обрезанная версия, но она работает.
Тестирование Плагинов
В нашем тестовом каталоге для pytest-nice есть два файла: conftest.py и test_nice.py . Чтобы использовать pytester , нам нужно добавить только одну строку в conftest.py :
Эта строка включает плагин pytester . Мы будем использовать фикстуру под названием testdir , которая становится доступным, когда pytester включен.
Часто тесты для плагинов принимают форму, которую мы описали вручную:
- Сделайте пример тестового файла.
- Запустите pytest с некоторыми параметрами или без них в каталоге, который содержит файл примера.
- Проверьте выходные данные.
- Возможный, для проверки кода результат-0 для всех проходов, 1 для некоторых сбоев.
Давайте рассмотрим один пример:
Фикстура testdir автоматически создает временный каталог для размещения тестовых файлов. Она имеет метод makepyfile() , который позволяет поместить содержимое тестового файла.В этом случае мы создаем два теста: один, который проходит и другой, который не проходит.
Обычно я смотрю на stdout и ret . Для проверки вывода, по аналогии с тем, как мы это делали вручную, используйте fnmatch_lines , передав список строк, которые мы хотим видеть в выводе, а затем убедившись, что ret равен 0 для проходящих сеансов и 1 для неудачных сеансов. Строки, передаваемые в fnmatch_lines , могут включать символы подстановки. Мы можем использовать наш пример файла для тестов. Вместо того, чтобы дублировать этот код, давайте напишем фикстуру:
Теперь, для остальных тестов, мы можем использовать sample_test в качестве каталога, который уже содержит наш пример тестового файла. Ниже приведены тесты для других вариантов:
Это могло бы быть частью других тестов, но мне нравится проводить его в отдельном тесте, чтобы один тест проверял одну задачу.
Давайте проверим текст справки:
Я думаю, что это достаточно хорошая проверка, того, что наш плагин работает.
Для запуска тестов давайте начнем с нашего каталога pytest-nice и убедимся, что наш плагин установлен. Мы делаем это либо установкой файла .zip.gz , либо установкой текущего каталога в редактируемом режиме:
Теперь, когда он установлен, давайте запустим тесты:
Прим. переводчика: Если вы потерпели неудачу, то вы не одиноки. У меня тесты не прошли сразу.
Я исправил шаблон поиска
Добавил символ * после F
По аналогии я внес исправления в test_with_nice , test_with_nice_verbose , test_not_nice_verbose
RemovedInPytest4Warning: usage of Session.Class is deprecated, please use pytest.Class instead
В остальном всё нормуль!
Ура! Все тесты пройдены. Мы можем удалить его (pytest-nice), как и любой другой пакет Python
или pytest-плагин:
Создание дистрибутива
Верьте или нет но, мы почти закончили с нашим плагином. Теперь, из командной строки мы можем использовать этот файл setup.py для создания дистрибутива:
В pytest-nice каталог dist содержит новый файл с именем pytest-nice-0.1.0.tar.gz .
Этот файл теперь может быть использован в любом месте, чтобы установить наш плагин, даже на месте:
Теперь вы можете поместить свои файлы .tar.gz в любое место, где сможете их использовать и делиться ими.
Распространение плагинов через общий каталог
pip уже поддерживает установку пакетов из общих каталогов, поэтому все, что нам нужно сделать, чтобы распространить наш плагин через общий каталог, это выбрать место, которое мы можем запомнить, и поместить туда файлы .tar.gz для наших плагинов. Допустим, мы поместили pytest-nice-0.1.0.tar.gz в каталог с именем myplugins .
Чтобы установить pytest-nice из myplugins :
Если вы исправили какие то ошибки и в myplugins есть более новые версии, вы можете обновить их, добавив --upgrade :
Это аналогично любому другому использованию pip , но с добавлением --no-index --find-links myplugins .
Распространение плагинов через PyPI
Если вы хотите поделиться своим плагином с миром, есть еще пару шагов, которые мы должны сделать. На самом деле, есть еще несколько шагов. Однако, поскольку эта книга не посвящена работе с открытым исходным кодом, я рекомендую ознакомиться с подробными инструкциями, содержащимися в руководстве пользователя Python Packaging.
Этот проект сначала задаст вам несколько вопросов о вашем плагине. Затем он создает каталог для вас, чтобы исследовать и заполнить ваш код. Углубление в принципы его работы выходит за рамки этой книги; однако, пожалуйста, имейте это в виду проект. Он поддерживается основными разработчиками pytest, и они будут следить за тем, чтобы этот проект оставался в актуальном состоянии.
Читайте также: