pytest-pycharm 0.7.0
You are ready to go right after the plugin installation. Execute any test in PyCharm by right-clicking on a test and selecting Debug pytest in …. The debugger will pause on a false assertion (see test example).
The plugin also works with custom pytest Run Configurations: Run | Edit Configurations | Add New Configuration | Python tests | pytest.
Note: make sure the Run Configuration uses the correct interpreter with pytest-pycharm installed.
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
- Go to the Python Package tool window
- 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 sample. Let’s create a pytest test to check the brake function.
Create a test

- Create a Python project.
- From the main menu, click File | New , choose Python file , type Car.py , and click OK .
- Copy and paste the Car sample into the Car.py file.
- In the editor, place the caret at the brake method declaration.
- 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.
In 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_pytest.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.
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.

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
- Click to run the test:

- Note that PyCharm automatically creates a pytest Run/Debug configuration:
Select Run pytest for test_car_pytest to execute the test. - Inspect test results:
To 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. - Alter the assert statement to the following: assert my_car.speed == 4599 .
- Rerun the test to evaluate the assert failing report:
Note 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
- Modify your Car 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
- let’s create a set of speed values to test car.accelerate and car.brake functions: speed_data =
- 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_accelerate45,>
Note that PyCharm detects the newly created parameters and adds them to the completion list. 

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 sample to validate the car’s speed. Add the following function to the Car.py file:
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.
Inspect the test_car_pytest_bdd_fixture.py file. It contains all required import statements and definitions for each scenario step.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()

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 есть несколько пакетов, позволяющих автоматизировать процесс тестирования.
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
Тема контроля качества очень обширна. И даже к написанному мной коду очень легко придраться. Как минимум здесь отсутствует проверка на то, что вводимые данные обязательно должны быть целыми числами. Если ввести любое другое число или даже строку, она обязательно завершится с ошибкой.
Кстати, в этой программе я намеренно оставил ещё одну ошибку (на сей раз уже логическую), связанную с нахождением корня. Напишите в комментариях, с чем она может быть связана, и какой тест поможет её отловить ?
