Тестирование -- это практика написания кода, который проверяет корректность другого кода. И это нечто большее: тесты дают уверенность в том, что код *можно менять*. При рефакторинге, исправлении бага или добавлении новой функции тест-сьют немедленно сигнализирует, если что-то сломалось где-то ещё. Без тестов каждое изменение несёт в себе скрытый риск.
Простейший тест состоит из трёх шагов:
1. Вызвать производственную функцию с конкретными входными данными
2. Получить результат
3. Утверждать, что результат равен ожидаемому
Если утверждение верно -- тест проходит. Если нет -- pytest точно показывает, в чём расхождение.
Ключевое слово `assert` -- встроенный механизм Python для этого: оно вычисляет выражение и выбрасывает `AssertionError`, если результат ложный. В тесте пишется `assert result == expected` -- вот и весь фундамент. Следующая тема (*Утверждения в деталях*) объясняет, как pytest кардинально улучшает вывод `assert`, и рассматривает паттерны для сравнения чисел с плавающей точкой, списков, словарей и другого.
**Почему pytest, а не unittest?**
Стандартная библиотека Python содержит `unittest`, но он многословен. Нужно наследоваться от `TestCase`, использовать `self.assertEqual()` вместо обычного `assert` и заворачивать всё в классы. pytest ничего этого не требует:
```python
# unittest -- много церемоний
import unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
# pytest -- просто функция
def test_add():
assert add(2, 3) == 5
```
Помимо синтаксиса, сообщения об ошибках pytest гораздо информативнее -- он показывает фактические значения, которые сравнивались. А фикстуры, parametrize и экосистема плагинов pytest несравнимо лучше.
**Установка:**
```bash
pip install pytest
```
**Как работает обнаружение тестов:**
pytest обходит директорию проекта и собирает тесты по следующим правилам именования:
- Файлы: `test_*.py` или `*_test.py`
- Функции на уровне модуля: начинаются с `test_`
- Классы: начинаются с `Test` (без метода `__init__`)
- Методы внутри классов `Test*`: начинаются с `test_`
Всё остальное игнорируется. `helper.py`, `utils.py`, `main.py` -- не собираются. Функция `check_result()` -- не собирается. Только паттерн `test_*` запускает сбор.
**Запуск тестов:**
```bash
pytest # запустить все найденные тесты
pytest -v # подробный вывод: показывать каждое имя теста и результат
pytest -s # показывать вывод print() (не подавлять stdout)
pytest --tb=short # короткие трассировки при падении
pytest --collect-only # показать, что будет запущено, без запуска
```
```bash
pytest # запустить все тесты
pytest -v # подробный вывод: показывать имя каждого теста
pytest -s # показывать вывод print() (не перехватывать stdout)
pytest test_foo.py # запустить один файл
pytest -k "add" # запустить тесты, в имени которых есть "add"
pytest --tb=short # короткие трассировки
pytest --tb=no # без трассировок (только счётчик пройденных/упавших)
```
**Правила именования, которым следует pytest:**
- Файлы: `test_*.py` или `*_test.py`
- Функции и методы: `test_*`
- Классы: `Test*` (без метода `__init__`)
**Что выводит pytest:**
```
collected 4 items
test_math_utils.py::test_add_positive_numbers PASSED
test_math_utils.py::test_add_negative PASSED
test_math_utils.py::test_clamp_within_range PASSED
test_math_utils.py::test_clamp_below_lower PASSED
4 passed in 0.05s
```
**Когда добавлять `import pytest`:**
Простые тесты требуют только `assert` и собственных импортов -- `import pytest` не нужен. Он добавляется при использовании специфичных для pytest возможностей: `pytest.approx` (сравнение чисел с плавающей точкой), `pytest.raises` (тестирование исключений), `@pytest.fixture` или меток вроде `@pytest.mark.parametrize`. Они рассматриваются в следующих темах.
Напишите тест-функцию `test_add`, которая вызывает `add(a, b)` как минимум с тремя разными парами входных данных и проверяет правильный результат для каждой.
# math_utils.py
def add(a, b):
return a + b
# test_math_utils.py
from math_utils import add
def test_add():
# проверить хотя бы три пары входных данных
pass
Напишите тест-функции для `is_even(n)`, которые проверяют, что функция возвращает `True` для чётных чисел и `False` для нечётных. Напишите как минимум две тест-функции -- одну для чётных входных данных, другую для нечётных.
Тест ниже содержит намеренный баг -- ожидаемое значение неправильное. Запустите pytest и прочитайте вывод падения, затем исправьте ожидаемое значение, чтобы тест прошёл.
def multiply(a, b):
return a * b
def test_multiply():
assert multiply(3, 4) == 11 # неправильное ожидаемое значение
assert multiply(0, 99) == 0
assert multiply(-2, 5) == -10
Напишите три отдельные тест-функции для `clamp(value, lo, hi)`: одну для значений внутри диапазона, одну для значений ниже нижней границы и одну для значений выше верхней границы.
Напишите тесты для `greet(name)`, которая возвращает `'Hello, {name}!'`. Проверьте, что возвращаемое значение начинается с 'Hello', заканчивается на '!' и содержит переданное имя.
def greet(name):
return f'Hello, {name}!'
def test_greet_alice():
result = greet('Alice')
assert result == 'Hello, Alice!'
assert result.startswith('Hello')
assert result.endswith('!')
assert 'Alice' in result
def test_greet_empty_name():
result = greet('')
assert result == 'Hello, !'
No split tab
Настройки cookies
Мы используем необходимые cookies для работы сайта. С вашего разрешения мы также можем сохранять настройки сайта и использовать аналитические и рекламные cookies, чтобы понимать использование сайта и поддерживать развитие проекта.
* Вы всегда можете изменить свой выбор в настройках сайта.
Выберите категории cookies
Настройки аналитики
Можно отключить аналитику использования платформы. Также можно отправить в Google Analytics запрос на удаление данных об использовании этого сайта, связанных с этим браузером.
Учебный workspace
Учитесь, читая, запуская код и решая задачи.
Практикуйте программирование с пояснениями тем, упражнениями, инструментами browser IDE, проверкой regex и тренировкой печати кода в одном workspace.
Открывайте инструменты во вкладках.Упражнения, IDE-инструменты и тренажеры остаются доступными как вкладки сайта.
Переключайтесь без потери контекста.Переходите между пояснениями, кодом и утилитами, сохраняя свое место.
Используйте sidebar как карту.Левые панели содержат навигацию, настройки, файлы, libraries и управление инструментами.
PythonJavaScriptSQLite
Одна IDE, три практичных режима
Python в браузере.Запускайте небольшие скрипты, пробуйте библиотеки и тренируйте API-запросы без установки.
JavaScript для быстрых экспериментов.Проверяйте код для браузера и сравнивайте идеи рядом с учебными материалами.
SQLite для практики с данными.Открывайте обозреватель базы данных, изучайте таблицы, пишите запросы и учитесь SQL локально.
ТемаIDE
Работайте рядом в split tabs
Держите инструкции перед глазами.Откройте упражнение или справочную страницу рядом с IDE, вместо постоянных переключений.
Сравнивайте инструменты во время обучения.Размещайте проверки regex, пояснения и эксперименты с кодом рядом, когда это нужно для задачи.
Закройте split, когда закончите.Workspace вернётся к одной сфокусированной вкладке, а открытые вкладки сайта останутся доступны.
Тренажер слепой печати кода
Или просто текста
Тренажер рассчитан на физическую клавиатуру.Откройте этот раздел на ноутбуке или компьютере с широким экраном. На телефоне тренировка слепой печати не будет корректной.
Скорость: 0 зн/мин
0 слов/мин
Лучшая скорость (60с): 0 зн/мин
0 слов/мин
Ошибки: 0
Общее время: 0.0 с
Для активации режима слепого набора не подсматривайте на физическую клавиатуру.