**Проблема:**
При тестировании функции с несколькими входами обычно пишут отдельные тест-функции:
```python
def test_square_positive(): assert square(3) == 9
def test_square_negative(): assert square(-4) == 16
def test_square_zero(): assert square(0) == 0
```
Это повторение. При изменении функции `square` придётся обновлять одну и ту же логику в нескольких местах. `@pytest.mark.parametrize` решает эту проблему: одна тест-функция запускается несколько раз с разными входными данными:
```python
import pytest
@pytest.mark.parametrize("n, expected", [
(3, 9),
(-4, 16),
(0, 0),
])
def test_square(n, expected):
assert square(n) == expected
```
Каждая строка создаёт независимый тест-кейс -- если `(3, 9)` упал, остальные всё равно выполняются. pytest выводит каждый отдельно.
**Разбор синтаксиса:**
```python
@pytest.mark.parametrize(
"param_names", # строка с именами параметров через запятую
[ # список значений (или кортежей для нескольких параметров)
value1,
value2,
]
)
def test_something(param_names):
...
```
Для одного параметра: плоский список. Для нескольких: список кортежей, по одному на тест-кейс.
**Как генерируются ID тестов:**
pytest автоматически создаёт имя для каждого кейса из значений параметров:
```
test_square[3-9] <- из (3, 9)
test_square[-4-16] <- из (-4, 16)
test_square[0-0] <- из (0, 0)
```
Можно переопределить через `ids=`:
```python
@pytest.mark.parametrize("n, expected", [
(3, 9),
(-4, 16),
], ids=["positive", "negative"])
# -> test_square[positive], test_square[negative]
```
**Когда параметризованный тест падает:**
pytest показывает точно, какой набор параметров это вызвал:
```
FAILED test_math.py::test_square[-4-16]
AssertionError: assert 4 == 16
```
Можно запустить только этот упавший кейс:
```bash
pytest -k "test_square[-4-16]"
```
**Когда НЕ стоит использовать parametrize:**
Если тест-кейсы имеют совершенно разную логику -- разные утверждения, разные вызовы функций -- они должны быть отдельными тест-функциями. Parametrize -- для одной и той же логики проверки с разными входными данными.
Напишите `square(n)`, которая возвращает `n * n`. Используйте `@pytest.mark.parametrize`, чтобы протестировать её минимум с 5 разными входными данными: положительное целое, отрицательное целое, ноль, число с плавающей точкой и большое число.
Напишите `is_palindrome(s)`, которая возвращает True, если строка одинаково читается в обоих направлениях. Используйте `@pytest.mark.parametrize` с минимум 4 палиндромами и 3 не-палиндромами, включая ожидаемое булево значение вторым параметром.
Используйте `@pytest.mark.parametrize` для тестирования `clamp(value, lo, hi)` минимум с 5 кейсами: значение внутри диапазона, на нижней границе, на верхней границе, ниже нижней границы и выше верхней. Используйте четыре параметра на строку: `value`, `lo`, `hi` и `expected`.
Параметризованный тест ниже автоматически генерирует бесполезные ID вроде `test_parse_int[42-42]`. Добавьте `ids=[...]` в декоратор с описательными именами. Запустите `pytest -v` до и после, чтобы увидеть разницу.
Напишите `is_valid_username(s)`, которая возвращает True, только если строка содержит от 3 до 20 символов, состоит только из букв и цифр и не начинается с цифры. Используйте `@pytest.mark.parametrize` для проверки минимум 3 допустимых и 4 недопустимых имён пользователей.
import pytest
def is_valid_username(s):
pass # реализуйте три правила валидации
@pytest.mark.parametrize("username, expected", [
# допустимые (True) и недопустимые (False) случаи
])
def test_is_valid_username(username, expected):
assert is_valid_username(username) == expected
Решение
import pytest
def is_valid_username(s):
if not s or not (3 <= len(s) <= 20):
return False
if s[0].isdigit():
return False
return all(c.isalnum() for c in s)
@pytest.mark.parametrize("username, expected", [
("alice", True),
("user123", True),
("A1b2C3", True),
("ab", False),
("a" * 21, False),
("1alice", False),
("alice!", False),
("", False),
])
def test_is_valid_username(username, expected):
assert is_valid_username(username) == expected
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 с
Для активации режима слепого набора не подсматривайте на физическую клавиатуру.