Как установить pytest в pycharm
Перейти к содержимому

Как установить pytest в pycharm

  • автор:

Pytest

PyCharm supports pytest, a fully functional testing framework.

The following features are available:

  • The dedicated test runner.
  • Code completion for test subject and pytest fixtures.
  • Code navigation.
  • Detailed failing assert reports.
  • Support for Python 2.7 and Python 3.5 and later.
  • Multiprocessing test execution.

By default, the suggested default test runner is unittest. So, to utilize pytest, you need to make it the default test runner first.

Install Pytest

Installing Pytest

  1. Go to the Python Package tool window
  2. Start typing pytest in the Search field of the Python Package tool window. Locate the package in the list of the available packages and click Install .

Once the pytest package is installed, PyCharm detects it and makes it the default project test runner. At any time you can change a test runner in the project settings.

To ensure all pytest-specific features are available, set the test runner manually: press Control+Alt+S to open the IDE settings and select Tools | Python Integrated Tools , and then select pytest from the Default test runner list.

Now, that pytest is set as the default testing framework, you can create a small test for the Car class. Let’s create a pytest test to check the brake function.

Create a test

Go to test

  1. Create a Python project.
  2. In the main menu, go to File | New , choose Python file , type car.py , and click OK .
  3. Copy and paste the Car class into the car.py file.
  4. In the editor, place the caret at the brake method declaration.
  5. Do one of the following:
    • Go to Navigate | Test .
    • From the context menu, choose Go To | Test .
    • Press Control+Shift+T .

PyCharm shows the list of available tests.

New test for pytest

  • Click Create new test . The Create Test dialog opens. Create a python test for the brake methodIn the Create Test dialog, specify the following settings:
    • Target directory, where the new test class will be generated.
    • The name of the test file (in our example, test_car.py ), and the name of the test class if needed.
    • Select the checkboxes next to the methods you want to include in the test class. Note that if you place the caret within a method, only this method name is included in the list. Also, mind the naming convention: the test method has the test prefix. The Run Test icon will appear in the editor gutter for all methods with such a prefix.
  • Click OK when ready. PyCharm generates the test file in the specified location.
  • PyCharm automatically generates a test file with the test method template. Replace the template code with the code that sets the initial speed value of Car to 50 and checks if speed gets properly set to 45 after the brake() function execution.

    from car import Car def test_car_brake(): car = Car(50) car.brake() assert car.speed == 45

    Note that PyCharm recognizes the test subject and offers completion for the Car class’ instance.

    Autocompletion for the test subject

    Although Go To Test Subject and Go To Test commands of the context menu are not supported for pytest, you can navigate to the tested code in car.py by using the Go To Declaration Control+B command.

    Run a test

    1. Click to run the test: Run the test
    2. Note that PyCharm automatically creates a pytest Run/Debug configuration: Suggested run/debug configuration for pytestSelect Run pytest for test_car_pytest to execute the test.
    3. Inspect test results: Pytest runTo manage the test output generated by pytest, press Control+Alt+S to open the IDE settings and select Advanced Settings , then select the Pytest: don’t add «—no-header —no-summary -q» checkbox in the Python section.
    4. Alter the assert statement to the following: assert my_car.speed == 4599 .
    5. Rerun the test to evaluate the assert failing report: Assert failing reportNote that pytest provides an explicit report on the failure.

    With pytest fixtures you can create small test units that can be reused across the testing module. All you need is to mark a reusable unit with @pytest.fixture .

    Use fixtures

    1. Modify your pytest test as follows:

    import pytest from car import Car @pytest.fixture def my_car(): return Car(50) def test_car_accelerate(my_car): my_car.accelerate() assert my_car.speed == 55 def test_car_brake(my_car): my_car.brake() assert my_car.speed == 45

    You can enable sharing fixture instances across tests using the scope parameter of the fixture function. For more information about pytest fixtures, refer to pytest fixtures documentation.

    You might want to run your tests on the predefined set of data. PyCharm supports test parametrization implemented in pytest through @pytest.mark.parametrize .

    Apply parametrization

    1. let’s create a set of speed values to test car.accelerate and car.brake functions: speed_data =
    2. Modify the test code to the following:

    import pytest from car import Car speed_data = <45, 50, 55, 100>@pytest.mark.parametrize(«speed_brake», speed_data) def test_car_brake(speed_brake): car = Car(50) car.brake() assert car.speed == speed_brake @pytest.mark.parametrize(«speed_accelerate», speed_data) def test_car_accelerate(speed_accelerate): car = Car(50) car.accelerate() assert car.speed == speed_accelerate

    Note that PyCharm detects the newly created parameters and adds them to the completion list.  Code completion for pytest.mark.parametrize

  • Run the test for the car.brake() function. You should expect the following test report: Three tests failed and one test passed for the car.brake function
  • You can also run the test for car.accelerate() function to ensure that it fails for all speed values but 55. For more information about parametrized test, refer to pytest documentation.

    If you use the Professional edition of PyCharm, in addition to the mentioned capabilities, you can use Behavior-Driven Development (BDD) through pytest_bdd . This is particularly helpful when you need to quickly record your test using the Gherkin language and utilize beneficial features of pytest, such as fixture .

    Implement test scenarios

    This procedure is applicable only for PyCharm Professional .

      let’s modify the Car class to validate the car’s speed. Add the following method:

    def speed_validate(self): return self.speed <= 160

    Feature: Speed Scenario: Valid speed Given Speed is less than 160 When Accelerated Then Speed is valid Scenario: Invalid speed Given Speed is more than 160 When Accelerated Then Speed is invalid

    Both scenarios validate the speed of the car. The speed is supposed to be valid when it does not exceed the value of 160. Note the scenario steps are highlighted because they are not defined by this moment. bdd scenarios

  • PyCharm enables quick generation of step definitions for the entire feature file. Place the caret at any of the highlighted steps, press Alt+Enter , and select Create all step definitions . In the opened dialog, specify the name of the test file (it should start with test ), select Python (pytest-bdd) from the File type list, and, if needed, modify the default file location. step definitions generationInspect the test_car_bdd.py file. It contains all required import statements and definitions for each scenario step.
  • let’s modify the test file to use a pytest fixture for a Car object and to add more test logic:

    from pytest_bdd import scenario, given, when, then import pytest from car import Car @pytest.fixture def my_car(): return Car() @scenario(‘car.feature’, ‘Valid speed’) def test_speed_valid(): pass @scenario(‘car.feature’, ‘Invalid speed’) def test_speed_invalid(): pass @given(«Speed is less than 160») def set_valid_speed(my_car): my_car.speed = 50 @given(«Speed is more than 160») def set_invalid_speed(my_car): my_car.speed = 100 @when(«Accelerated») def car_accelerate(my_car): my_car.accelerate() @then(«Speed is valid») def success(my_car): assert my_car.speed_validate() @then(«Speed is invalid») def fail(my_car): assert not my_car.speed_validate()

    pytest-bdd results

  • Run the test by creating the corresponding Run/Debug configuration. You can also run either of the scenarios using the Run icon in the gutter.
  • Inspect the test run results. In our example, we have the test_speed_valid test passed and the test_speed_invalid test failed.
  • By default, PyCharm provides the expected == actual assertion. To change the order of the assertion parameters, press Control+Alt+S to open the IDE settings and select Advanced Settings , then select the Swap the order of actual and expected assertions in Pytest checkbox in the Python section.

    Установка модулей в PyCharm

    Не получается установить библиотеки Python в PyCharm. Использую Python 3.10.6, windows 10 x64. Для примера — попробую установить requests через терминал PyCharm в свое виртуальное окружение. Такая ошибка:

    (venv) PS C:\Users\alexandrovn\PycharmProjects\pythonProject1> pip install requests WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(, 'Connection to py pi.org timed out. (connect timeout=15)')': /simple/requests/ WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(, 'Connection to py pi.org timed out. (connect timeout=15)')': /simple/requests/ WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(, 'Connection to py pi.org timed out. (connect timeout=15)')': /simple/requests/ WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(, 'Connection to py pi.org timed out. (connect timeout=15)')': /simple/requests/ WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ConnectTimeoutError(, 'Connection to py pi.org timed out. (connect timeout=15)')': /simple/requests/ ERROR: Could not find a version that satisfies the requirement requests (from versions: none) ERROR: No matching distribution found for requests 

    Также, я посмотрел несколько видео и попытался установить через Settings — Python Interpreter — Нажимаю на «+», над надписью package и в окне Available Packages идет обновление и появляется сообщение: Reload List of Packages Error updating package list: Connect timed out Описание Также я пробовал установить из вкладки View — Tool Windows — Python Packages(хотя там список библиотек очень маленький). Выбираю requests и результат такой: введите сюда описание изображения Помогите, я уже не понимаю, в чем проблема, обрыл все источники, которые находил. Ранее, я устанавливал Anaconda и делал ее основным Интерпретатором, в ней были некие библиотеки, в списке — Available Packages и я сразу их импортировал в проекты, но ни одну стороннюю библиотеку я не могу установить.

    Тестируем на Python: unittest и pytest. Инструкция для начинающих

    Python-разработчик Андрей Смирнов рассказал, как написать первые тесты и какие фреймворки выбрать: unittest или pytest.

    Меня зовут Андрей Смирнов, я занимаюсь Python-разработкой, автоматизацией технических процессов и преподаю промышленное программирование в Школе программистов МШП.

    Не секрет, что разработчики создают программы, которые рано или поздно становятся очень масштабными (если смотреть на количество строчек кода). А с этим приходит и большая ответственность за качество.

    Сейчас расскажу, как unittest и pytest помогут найти ошибки в программах и исключить их в будущем.

    Итак, тестирование

    Каждый, кто писал первые программы (будь то классический «hello, world» или же калькулятор), всегда запускал тесты, чтобы проверить их работу.

    Сам факт запуска — самое первое, незримое касание технологии тестирования в вашей жизни. Рассмотрим его как процесс поиска ошибок на чуть более сложной программе.

    Например, вам нужно ввести три числа (a, b, c) и найти корни квадратного уравнения. Для решения пишем код:

    from math import sqrt def square_eq_solver(a, b, c): result = [] discriminant = b * b - 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) else: result.append((-b + sqrt(discriminant)) / (2 * a)) result.append((-b - sqrt(discriminant)) / (2 * a)) return result def show_result(data): if len(data) > 0: for index, value in enumerate(data): print(f'Корень номер равен ') else: print('Уравнение с заданными параметрами не имеет корней') def main(): a, b, c = map(int, input('Пожалуйста, введите три числа через пробел: ').split()) result = square_eq_solver(a, b, c) show_result(result) if __name__ == '__main__': main() 

    Сразу оговорюсь: любую задачу, какой бы она ни была краткой, я рассматриваю с позиции «когда-нибудь она вырастет и станет очень объёмной». Поэтому всегда стараюсь разделять программу на различные подпрограммы (ввод/обработка/вывод).

    Возможно, вы уже заметили ошибку в коде. Однако иногда она может быть скрыта настолько глубоко, что её просто так не обнаружишь. И в таком случае единственный способ вывести ее на свет — протестировать код. Как это сделать?

    — зная алгоритм нахождения корней уравнения, определяем наборы входных данных, которые будут переданы на вход программе;

    — зная входные данные, можно вручную просчитать, какой ответ должна дать программа;

    — запускаем программу и передаем ей на вход исходные данные;

    — получаем от нее ответ и сравниваем с тем, который должен быть получен. Если они совпадают — хорошо, идём к следующему набору данных, если нет, сообщаем об ошибке.

    Например, для данной задачи можно подобрать следующие тесты:

    • 10x**2 = 0 — единственный корень x=0
    • 2x**2 + 5x — 3 = 0 — у такого уравнения два корня (x1 = 0.5, x2=-3)
    • 10x**2+2 = 0 — у этого уравнения корней нет

    Тесты подобрали, что дальше? Правильно, запускаем:

    Тест номер 1 > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 0 Корень номер 0 равен 0.00 Тест номер 2: > python.exe example.py Пожалуйста, введите три числа через пробел: 2 5 -3 Корень номер 1 равен 0.50 Корень номер 2 равен -3.00 Тест номер 3: > python.exe example.py Пожалуйста, введите три числа через пробел: 10 0 2 Traceback (most recent call last): File "C:PyProjectstprogerexample.py", line 32, in main() File "C:PyProjectstprogerexample.py", line 27, in main result = square_eq_solver(a, b, c) File "C:PyProjectstprogerexample.py", line 11, in square_eq_solver result.append((-b + sqrt(discriminant)) / (2 * a)) ValueError: math domain error 

    Упс… В третьем тесте произошла ошибка. Как раз та, которую вы могли заметить в исходном коде программы — не обрабатывался случай с нулевым дискриминантом. В итоге, можно подкорректировать код функции так, чтобы этот вариант обрабатывался правильно:

    def square_eq_solver(a, b, c): result = [] discriminant = b * b - 4 * a * c if discriminant == 0: result.append(-b / (2 * a)) elif discriminant > 0: # 

    Запускаем все тесты повторно и они срабатывают нормально.

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

    Программа автоматического тестирования запускается на основе заранее заготовленных входных/выходных данных и программы, которая будет их вызывать. По сути, это программа, тестирующая другие программы. И в рамках экосистемы языка Python есть несколько пакетов, позволяющих автоматизировать процесс тестирования.

    Что делает Python-разработчик в облаке

    Unittest и pytest: пишем тесты

    Две самые популярные библиотеки — unittest и pytest. Попробуем каждую, чтобы объективно оценить синтаксис.

    Начнем с unittest, потому что именно с нее многие знакомятся с миром тестирования. Причина проста: библиотека по умолчанию встроена в стандартную библиотеку языка Python.

    Формат кода

    По формату написания тестов она сильно напоминает библиотеку JUnit, используемую в языке Java для написания тестов:

    • тесты должны быть написаны в классе;
    • класс должен быть отнаследован от базового класса unittest.TestCase;
    • имена всех функций, являющихся тестами, должны начинаться с ключевого слова test;
    • внутри функций должны быть вызовы операторов сравнения (assertX) — именно они будут проверять наши полученные значения на соответствие заявленным.

    Пример использования unittest для нашей задачи

    import unittest class SquareEqSolverTestCase(unittest.TestCase): def test_no_root(self): res = square_eq_solver(10, 0, 2) self.assertEqual(len(res), 0) def test_single_root(self): res = square_eq_solver(10, 0, 0) self.assertEqual(len(res), 1) self.assertEqual(res, [0]) def test_multiple_root(self): res = square_eq_solver(2, 5, -3) self.assertEqual(len(res), 2) self.assertEqual(res, [0.5, -3]) 

    Запускается данный код следующей командой

    python.exe -m unittest example.py

    И в результате на экран будет выведено:

    > python.exe -m unittest example.py . ------------------------------------------------------------------ Ran 3 tests in 0.001s OK 

    В случае, если в каком-нибудь из тестов будет обнаружена ошибка, unittest не замедлит о ней сообщить:

    > python.exe -m unittest example.py F.. ================================================================== FAIL: test_multiple_root (hello.SquareEqSolverTestCase) ------------------------------------------------------------------ Traceback (most recent call last): File "C:PyProjectstprogerexample.py", line 101, in test_multiple_root self.assertEqual(len(res), 3) AssertionError: 2 != 3 ------------------------------------------------------------------ Ran 3 tests in 0.001s FAILED (failures=1) 

    Unittest: аргументы “за”

    • Является частью стандартной библиотеки языка Python: не нужно устанавливать ничего дополнительно;
    • Гибкая структура и условия запуска тестов. Для каждого теста можно назначить теги, в соответствии с которыми будем запускаться либо одна, либо другая группа тестов;
    • Быстрая генерация отчетов о проведенном тестировании, как в формате plaintext, так и в формате XML.

    Unittest: аргументы “против”

    • Для проведения тестирования придётся написать достаточно большое количество кода (по сравнению с другими библиотеками);
    • Из-за того, что разработчики вдохновлялись форматом библиотеки JUnit, названия основных функций написаны в стиле camelCase (например setUp и assertEqual);
    • В языке python согласно рекомендациям pep8 должен использоваться формат названий snake_case (например set_up и assert_equal).

    Pytest

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

    Pytest позволяет провести модульное тестирование (тестирование отдельных компонентов программы), функциональное тестирование (тестирование способности кода удовлетворять бизнес-требования), тестирование API (application programming interface) и многое другое.

    Формат кода

    Написание тестов здесь намного проще, нежели в unittest. Вам нужно просто написать несколько функций, удовлетворяющих следующим условиям:

    • Название функции должно начинаться с ключевого слова test;
    • Внутри функции должно проверяться логическое выражение при помощи оператора assert.

    Пример использования pytest для нашей задачи:

    def test_no_root(): res = square_eq_solver(10, 0, 2) assert len(res) == 0 def test_single_root(): res = square_eq_solver(10, 0, 0) assert len(res) == 1 assert res == [0] def test_multiple_root(): res = square_eq_solver(2, 5, -3) assert len(res) == 3 assert res == [0.5, -3] 

    Запускается данный код следующей командой

    pytest.exe example.py

    И в результате на экран будет выведено:

    > pytest.exe example.py ======================= test session starts ====================== platform win32 -- Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py . [100%] ======================== 3 passed in 0.03s ======================= 

    В случае ошибки вывод будет несколько больше:

    > pytest.exe example.py ======================= test session starts ====================== platform win32 -- Python 3.9.6, pytest-7.1.2, pluggy-1.0.0 rootdir: C:PyProjectstproger collected 3 items example.py ..F [100%] ============================ FAILURES ============================ _______________________ test_multiple_root _______________________ def test_multiple_root(): res = square_eq_solver(2, 5, -3) > assert len(res) == 3 E assert 2 == 3 E + where 2 = len([0.5, -3.0]) example.py:116: AssertionError ===================== short test summary info ==================== FAILED example.py::test_multiple_root - assert 2 == 3 =================== 1 failed, 2 passed in 0.10s ================== 

    Pytest: аргументы “за”

    • Позволяет писать компактные (по сравнению с unittest) наборы тестов;
    • В случае возникновения ошибок выводится гораздо больше информации о них;
    • Позволяет запускать тесты, написанные для других тестирующих систем;
    • Имеет систему плагинов (и сотни этих самых плагинов), расширяющую возможности фреймворка. Примеры таких плагинов: pytest-cov, pytest-django, pytest-bdd;
    • Позволяет запускать тесты в параллели (при помощи плагина pytest-xdist).

    Pytest: аргументы “против”

    • pytest не входит в стандартную библиотеку языка Python. Поэтому его придётся устанавливать отдельно при помощи команды pip install pytest;
    • совместимость кода с другими фреймворками отсутствует. Так что, если напишете код под pytest, запустить его при помощи встроенного unittest не получится.

    Ну и что лучше?

    • Если вам нужно базовое юнит-тестирование и вы знакомы с фреймворками вида xUnit, тогда вам подойдёт unittest.
    • Если нужен фреймворк, позволяющий создавать краткие и изящные тесты, реализующие сложную логику проверок, то pytest.

    Post Scriptum

    Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.

    Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить ?

    Использование и вызов¶

    Тестирование можно запустить из командной строки интерпретатора Python , используя команду:

    python -m pytest [. ]

    В отличие от запуска напрямую командой pytest [. ] , запуск через Python добавит текущий каталог в sys.path .

    Статусы завершения¶

    Выполнение pytest может генерировать один из следующих статусов завершения:

    Все тесты были собраны и успешно прошли

    Тесты были собраны и запущены, но некоторые из них упали

    Выполнение тестов было прервано пользователем

    Во время выполнения тестов произошла внутренняя ошибка

    Ошибка запуска pytest из командной строки

    Не удалось собрать тесты (тесты не найдены)

    Коллекция статусов представлена перечислением _pytest.config.ExitCode. Статусы завершения являются частью публичного API, их можно импортировать и использовать непосредственно:

    from pytest import ExitCode 

    Для настройки кода завершения сценария, особенно когда тесты не удалось собрать, можно использовать плагин pytest-custom_exit_code.

    Получение помощи по версии, параметрам, переменным окружения¶

    pytest --version # показывает версию и место, откуда импортирован ``pytest`` pytest --fixtures # показывает доступные встроенные функции pytest -h | --help # показывает помощь по командной строке и параметры конфигруационного файла 

    Остановка после первых N падений¶

    Чтобы остановить процесс тестирования после первых N падений, используются параметры:

    pytest -x # остановка после первого упавшего теста pytest --maxfail=2 # остановка после первых двух упавших тестов 

    Выбор выполняемых тестов¶

    pytest поддерживает несколько способов выбора и запуска тестов из командной строки.

    Запуск тестов модуля

    pytest test_mod.py

    Запуск тестов из директории

    pytest testing/

    Запуск тестов, удовлетворяющих ключевому выражению

    pytest -k "MyClass and not method" 

    Эта команда запустит тесты, имена которых удовлетворяют заданному строковому выражению (без учета регистра). Строковые выражения могут включать операторы Python , которые используют имена файлов, классов и функций в качестве переменных. В приведенном выше примере будет запущен тест MyClass.test_something , но не будет запущен тест TestMyClass.test_method_simple .

    Запуск тестов по идентификаторам узлов

    Каждому собранному тесту присваивается уникальный идентификатор nodeid , который состоит из имени файла модуля, за которым следуют спецификаторы, такие как имена классов, имена функций и параметры из параметризации, разделенные символами :: :

    Чтобы запустить конкретный тест из модуля, выполните:

    pytest test_mod.py::test_func

    Еще один пример спецификации тестового метода в командной строке:

    pytest test_mod.py::TestClass::test_method

    Запуск маркированных тестов

    pytest -m slow

    Будут запущены тесты, помеченные декоратором @pytest.mark.slow .

    Запуск тестов из пакетов

    pytest --pyargs pkg.testing

    Будет импортирован пакет pkg.testing , и его расположение в файловой системе будет использовано для поиска и запуска тестов.

    Изменение вывода сообщений трассировки¶

    pytest --showlocals # показывать локальные переменные в сообщениях pytest -l # показывать локальные переменные в сообщениях (краткий вариант) pytest --tb=auto # (по умолчанию) "расширенный" вывод для первого и # последнего сообщений, и "короткий" для остальных pytest --tb=long # исчерпывающий, подробный формат сообщений pytest --tb=short # сокращенный формат сообщений pytest --tb=line # только одна строка на падение pytest --tb=native # стандартный формат библиотеки Python pytest --tb=no # никаких сообщений 

    Использование --full-trace приводит к тому, что при ошибке печатаются очень длинные трассировки (длиннее, чем при --tb=long ). Параметр также гарантирует, что сообщения трассировки будут напечатаны при прерывании выполнения c клавиатуры с помощью Ctrl+C. Это очень полезно, если тесты занимают слишком много времени, и вы прерываете их с клавиатуры с помощью Ctrl+C, чтобы узнать, где они зависли. По умолчанию при прерывании вывод не будет показан (поскольку исключение KeyboardInterrupt будет поймано pytest ). Используя этот параметр, вы можете быть уверены, что увидите трассировку.

    Детализация сводного отчета¶

    Флаг -r можно использовать для отображения «краткой сводной информации по тестированию» в конце тестового сеанса, что упрощает получение четкой картины всех сбоев, пропусков, xfails и т. д.

    По умолчанию для списка сбоев и ошибок используется добавочная комбинация fE .

    # content of test_example.py import pytest @pytest.fixture def error_fixture(): assert 0 def test_ok(): print("ok") def test_fail(): assert 0 def test_error(error_fixture): pass def test_skip(): pytest.skip("skipping this test") def test_xfail(): pytest.xfail("xfailing this test") @pytest.mark.xfail(reason="always xfail") def test_xpass(): pass 
    $ pytest -ra =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ========================= short test summary info ========================== SKIPPED [1] $REGENDOC_TMPDIR/test_example.py:22: skipping this test XFAIL test_example.py::test_xfail reason: xfailing this test XPASS test_example.py::test_xpass always xfail ERROR test_example.py::test_error - assert 0 FAILED test_example.py::test_fail - assert 0 == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === 

    Параметр -r принимает ряд символов после себя. Использованный выше символ а означает “все, кроме успешных».

    Вот полный список доступных символов, которые можно использовать:

    • f - упавшие (добавляет раздел FAILED)
    • E - ошибки (добавляет раздел ERROR)
    • s - пропущенные (добавляет раздел SKIPPED)
    • x - тесты XFAIL (добавляет раздел XFAIL)
    • X - тесты XPASS (добавляет раздел XPASS)
    • p - успешные (passed)
    • P - успешные (passed) с выводом

    Есть и специальные символы для пропуска отдельных групп:

    • a - выводить все, кроме pP
    • A - выводить все
    • N - ничего не выводить (может быть полезным, поскольку по умолчанию используется комбинация fE ).

    Можно использовать более одного символа. Например, для того, чтобы увидеть только упавшие и пропущенные тесты, можно выполнить:

    $ pytest -rfs =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ========================= short test summary info ========================== FAILED test_example.py::test_fail - assert 0 SKIPPED [1] $REGENDOC_TMPDIR/test_example.py:22: skipping this test == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === 

    Использование p добавляет в сводный отчет успешные тесты, а P добавляет дополнительный раздел «пройдены” (PASSED) для тестов, которые прошли, но перехватили вывод:

    $ pytest -rpP =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-5.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items test_example.py .FEsxX [100%] ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError ================================== PASSES ================================== _________________________________ test_ok __________________________________ --------------------------- Captured stdout call --------------------------- ok ========================= short test summary info ========================== PASSED test_example.py::test_ok == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === 

    Запуск отладчика PDB (Python Debugger) при падении тестов¶

    python содержит встроенный отладчик PDB (Python Debugger). pytest позволяет запустить отладчик с помощью параметра командной строки:

    pytest --pdb

    Использование параметра позволяет запускать отладчик при каждом падении теста (или прерывании его с клавиатуры). Часто хочется сделать это для первого же упавшего теста, чтобы понять причину его падения:

    pytest -x --pdb # вызывает отладчик при первом падении и завершает тестовую сессию pytest --pdb --maxfail=3 # вызывает отладчик для первых трех падений 

    Обратите внимание, что при любом падении информация об исключении сохраняется в sys.last_value , sys.last_type и sys.last_traceback . При интерактивном использовании это позволяет перейти к отладке после падения с помощью любого инструмента отладки. Можно также вручную получить доступ к информации об исключениях, например:

    >>> import sys >>> sys.last_traceback.tb_lineno 42 >>> sys.last_value AssertionError('assert result == "ok"',) 

    Запуск отладчика PDB (Python Debugger) в начале теста¶

    pytest позволяет запустить отладчик сразу же при старте каждого теста. Для этого нужно передать следующий параметр:

    pytest --trace

    В этом случае отладчик будет вызываться при запуске каждого теста.

    Установка точек останова¶

    Чтобы установить точку останова, вызовите в коде import pdb;pdb.set_trace() , и pytest автоматически отключит перехват вывода для этого теста, при этом:

    • на перехват вывода в других тестах это не повлияет;
    • весь перехваченный ранее вывод будет обработан как есть;
    • перехват вывода возобновится после завершения отладочной сессии (с помощью команды continue ).

    Использование встроенной функции breakpoint¶

    Python 3.7 содержит встроенную функцию breakpoint() . pytest поддерживает использование breakpoint() следующим образом:

    • если вызывается breakpoint() , и при этом переменная PYTHONBREAKPOINT установлена в значение по умолчанию, pytest использует расширяемый отладчик PDB вместо системного;
    • когда тестирование будет завершено, система снова будет использовать отладчик Pdb по умолчанию;
    • если pytest вызывается с опцией --pdb то расширяемый отладчик PDB используется как для функции breakpoint() , так и для упавших тестов/необработанных исключений;
    • для определения пользовательского класса отладчика можно использовать --pdbcls .

    Профилирование продолжительности выполнения теста¶

    Чтобы получить список 10 самых медленных тестов, выполните:

    pytest --durations=10 

    По умолчанию, pytest не покажет тесты со слишком маленькой (менее одной сотой секунды) длительностью выполнения, если в командной строке не будет передан параметр -vv .

    Модуль faulthandler ¶

    Стандартный модуль faulthandler можно использовать для сброса трассировок Python при ошибке или по истечении времени ожидания.

    При запуске pytest модуль автоматически подключается, если только в командной строке не используется опция -p no:faulthandler .

    Кроме того, для сброса трассировок всех потоков в случае, когда тест длится более X секунд, можно использовать опцию faulthandler_timeout=X (для Windows неприменима).

    Эта функциональность была интегрирована из внешнего плагина pytest-faulthandler с двумя небольшими изменениями:

    • чтобы ее отключить, используйте -p no:faulthandler вместо --no-faulthandler ;
    • опция командной строки --faulthandler-timeout превратилась в конфигурационную опцию faulthandler_timeout. Ее по-прежнему можно настроить из команндной строки, используя -o faulthandler_timeout=X .

    Создание файлов формата JUnit¶

    Чтобы создать результирующие файлы в формате, понятном Jenkins или другому серверу непрерывной интеграции, используйте вызов:

    pytest --junitxml=path

    Команда создает xml-файл по указанному пути.

    Чтобы задать имя корневого xml-элемента для набора тестов, можно настроить параметр junit_suite_name в конфигурационном файле:

    [pytest] junit_suite_name = my_suite 

    Спецификация JUnit XML, по-видимому, указывает, что атрибут "time" должен сообщать об общем времени выполнения теста, включая выполнение setup- и teardown- методов (1, 2). Это поведение pytest по умолчанию. Чтобы вместо этого сообщать только о длительности вызовов, настройте параметр junit_duration_report следующим образом:

    [pytest] junit_duration_report = call 

    record_property¶

    Чтобы записать дополнительную информацию для теста, используйте фикстуру record_property :

    def test_function(record_property): record_property("example_key", 1) assert True 

    Такая запись добавит дополнительное свойство example_key="1" к сгенерированному тегу testcase :

     classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">  name="example_key" value="1" />   

    Эту функциональность также можно использовать совместно с пользовательскими маркерами:

    # content of conftest.py def pytest_collection_modifyitems(session, config, items): for item in items: for marker in item.iter_markers(name="test_id"): test_id = marker.args[0] item.user_properties.append(("test_id", test_id)) 
    # content of test_function.py import pytest @pytest.mark.test_id(1501) def test_function(): assert True 

    В файле получим:

     classname="test_function" file="test_function.py" line="0" name="test_function" time="0.0009">  name="test_id" value="1501" />   

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

    record_xml_attribute¶

    Чтобы добавить дополнительный атрибут в элемент testcase , можно использовать фикстуру record_xml_attribute . Ее также можно использовать для переопределения существующих значений:

    def test_function(record_xml_attribute): record_xml_attribute("assertions", "REQ-1234") record_xml_attribute("classname", "custom_classname") print("hello world") assert True 

    В отличие от record_property , дочерний элемент в данном случае не добавляется. Вместо этого в элемент testcase будет добавлен атрибут assertions="REQ-1234" , а значение атрибута classname по умолчанию будет заменено на "classname=custom_classname" :

     classname="custom_classname" file="test_function.py" line="0" name="test_function" time="0.003" assertions="REQ-1234"> hello world  

    record_xml_attribute пока используется в режиме эксперимента, и в будущем может быть заменен чем-то более мощным и/или общим. Однако сама функциональность как таковая будет сохранена.

    Использование record_xml_attribute поверх record_xml_property может быть полезным при парсинге xml-отчетов средствами непрерывной интеграции. Однако некоторые парсеры допускают не любые элементы и атрибуты. Многие инструменты (как в примере ниже), используют xsd-схему для валидации входящих xml. Поэтому убедитесь, что имена атрибутов, которые вы используете, являются допустимыми для вашего парсера.

    Ниже представлена схема, которую использует Jenkins для валидации xml-отчетов:

     name="testcase">   ref="skipped" minOccurs="0" maxOccurs="1"/>  ref="error" minOccurs="0" maxOccurs="unbounded"/>  ref="failure" minOccurs="0" maxOccurs="unbounded"/>  ref="system-out" minOccurs="0" maxOccurs="unbounded"/>  ref="system-err" minOccurs="0" maxOccurs="unbounded"/>   name="name" type="xs:string" use="required"/>  name="assertions" type="xs:string" use="optional"/>  name="time" type="xs:string" use="optional"/>  name="classname" type="xs:string" use="optional"/>  name="status" type="xs:string" use="optional"/>   

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

    Чтобы добавить свойства каждому тесту из набора, можно использовать фикстуру record_testsuite_property с параметром scope="session" (в этом случае она будет применяться ко всем тестам тестовой сессии).

    import pytest @pytest.fixture(scope="session", autouse=True) def log_global_env_facts(record_testsuite_property): record_testsuite_property("ARCH", "PPC") record_testsuite_property("STORAGE_TYPE", "CEPH") class TestMe: def test_foo(self): assert True 

    Этой фикстуре передаются имя ( name ) и значение ( value ) тэга , который добавляется на уровне тестового набора для генерируемого xml-файла:

     errors="0" failures="0" name="pytest" skipped="0" tests="1" time="0.006">  name="ARCH" value="PPC"/>  name="STORAGE_TYPE" value="CEPH"/>   classname="test_me.TestMe" file="test_me.py" line="16" name="test_foo" time="0.000243663787842"/>  

    name должно быть строкой, а value будет преобразовано в строку и корректно экранировано.

    В отличие от случаев использования record_property и record_xml_attribute созданный xml-файл будет совместим с последним стандартом xunit .

    Создание файлов в формате resultlog¶

    Для создания машиночитаемых логов в формате plain-text можно выполнить

    pytest --resultlog=path

    и просмотреть содержимое по указанному пути path . Эти файлы также используются ресурсом PyPy-test для отображения результатов тестов после ревизий.

    Поскольку эта возможность редко используется, она запланирована к удалению в pytest 6.0 .

    Если вы пользуетесь ею, рассмотрите возможность использования нового плагина pytest-reportlog .

    Отправка отчетов на сервис pastebin¶

    Создание ссылки для каждого упавшего теста:

    pytest --pastebin=failed

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

    Создание ссылки для лога тестовой сессии:

    pytest --pastebin=all

    В настоящее время реализовна регистрация только в сервисе http://bpaste.net.

    Изменено в вресии 5.2

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

    Подключение плагинов¶

    В командной строке можно явно подгрузить какой-либо внутренний или внешний плагин, используя опцию -p :

    pytest -p mypluginmodule 

    Опция принимает параметр name , который может быть:

    • Полным именем модуля, записанным через точку, например myproject.plugins . Имя должно быть импортируемым.
    • «Входным» именем плагина, которое передается в setuptools при регистрации плагина. К примеру, чтобы подгрузить pytest-cov , нужно использовать:

    pytest -p pytest_cov 

    Отключение плагинов¶

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

    Пример: чтобы отключить загрузку плагина doctest , который отвечает за выполнение тестов из строк «docstring», вызовите pytest следующим образом:

    pytest -p no:doctest

    Вызов pytest из кода Python¶

    pytest можно вызвать прямо в коде Python:

    pytest.main() 

    Такой способ эквивалентен вызову «pytest» из командной строки. В этом случае вместо исключения SystemExit возвращается статус завершения. Можно также передавать параметры и опции:

    pytest.main(["-x", "mytestdir"]) 

    Дополнительные плагины можно указать в pytest.main :

    # content of myinvoke.py import pytest class MyPlugin: def pytest_sessionfinish(self): print("*** test run reporting finishing") pytest.main(["-qq"], plugins=[MyPlugin()]) 

    Выполнив этот код, увидим, что MyPlugin был загружен и применен:

    $ python myinvoke.py .FEsxX. [100%]*** test run reporting finishing ================================== ERRORS ================================== _______________________ ERROR at setup of test_error _______________________ @pytest.fixture def error_fixture(): > assert 0 E assert 0 test_example.py:6: AssertionError ================================= FAILURES ================================= ________________________________ test_fail _________________________________ def test_fail(): > assert 0 E assert 0 test_example.py:14: AssertionError

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

    pytest

    • Что такое pytest
    • Установка
    • Содержание
    • Примеры
    • Конфигурирование
    • Лицензия
    • Использование и вызов
      • Вызов pytest с помощью python -m pytest
      • Статусы завершения
      • Получение помощи по версии, параметрам, переменным окружения
      • Остановка после первых N падений
      • Выбор выполняемых тестов
      • Изменение вывода сообщений трассировки
      • Детализация сводного отчета
      • Запуск отладчика PDB (Python Debugger) при падении тестов
      • Запуск отладчика PDB (Python Debugger) в начале теста
      • Установка точек останова
      • Использование встроенной функции breakpoint
      • Профилирование продолжительности выполнения теста
      • Модуль faulthandler
      • Создание файлов формата JUnit
        • record_property
        • record_xml_attribute

        Навигация:

    Добавить комментарий

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