✅ Решения заданий

⚡ Ответы кратко

  • Файлы: test_*.py или *_test.py. Функции: def test_*.
  • Фикстура: @pytest.fixture def calculator(): return Calculator()
  • Исключение: with pytest.raises(ArithmeticError, match="..."): ...
  • Маркировки: @pytest.mark.skip / skipif / positive_test

Решение задания 1: Правила именования

Файлы для тестирования должны начинаться с test_ или заканчиваться на _test. Функции тестов должны начинаться с test_.

# Правильные имена файлов:
test_calculator.py
calculator_test.py

# Правильная функция теста:
def test_sum():
    calculator = Calculator()
    assert calculator.sum(4, 5) == 9

# Неправильно (pytest не найдёт):
def check_sum():       # нет префикса test_
    assert True

def TestSum():          # PascalCase не распознаётся
    assert True

Решение задания 2: Фикстура Calculator

# test_calculator.py
import pytest
from calculator import Calculator


@pytest.fixture
def calculator():
    """Создаёт новый экземпляр Calculator для каждого теста."""
    return Calculator()


# Фикстура передаётся как аргумент — pytest внедряет её автоматически
def test_sum(calculator):
    assert calculator.sum(4, 5) == 9

def test_avg(calculator):
    numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 5]
    assert calculator.avg(numbers) == 5
Почему фикстура лучше, чем создание объекта в каждом тесте? При изменении конструктора Calculator достаточно обновить только фикстуру — все тесты подхватят изменение автоматически.

Решение задания 3: Проверка исключения

def test_division_by_zero(calculator):
    # pytest.raises() — контекстный менеджер для проверки исключений
    # match= проверяет текст сообщения через re.search
    with pytest.raises(ArithmeticError, match="На ноль делить нельзя"):
        calculator.div(10, 0)

# В этом примере проверяется:
# 1. Что ArithmeticError действительно поднимается
# 2. Что текст ошибки содержит "На ноль делить нельзя"
# Если исключение НЕ поднято — тест упадёт с Failed

Решение задания 4: Маркировки

import sys
import pytest
from calculator import Calculator


# 1. Пропуск теста
@pytest.mark.skip(reason="Тест временно отключён")
def test_skip_example():
    assert True


# 2. Условный пропуск
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Требуется Python 3.8")
def test_version_dependent():
    calculator = Calculator()
    assert calculator.sum(4, 5) == 9


# 3. Пользовательская маркировка
@pytest.mark.positive_test
def test_sum():
    calculator = Calculator()
    assert calculator.sum(4, 5) == 9

@pytest.mark.positive_test
def test_div():
    calculator = Calculator()
    assert calculator.div(10, 2) == 5
# Запуск тестов по маркировке:
pytest -m positive_test

# pytest.ini — регистрация маркера (избегает предупреждений):
[pytest]
markers =
    positive_test: Тесты для позитивных сценариев

Решение задания 5: Преимущества pytest

  • Скорость выполнения: тесты запускаются быстро, особенно при повторных запусках в CI.
  • Устранение человеческого фактора: автоматизация исключает ошибки ручного выполнения тестов.
  • Удобное управление тестами: группировка с помощью маркировок и фильтры (-k, -m).
  • Генерация отчётов: pytest предоставляет детализированные отчёты о результатах тестов.
  • Простота написания: минимальная настройка, лаконичный синтаксис — нет необходимости наследоваться от TestCase.
  • Изоляция тестов через фикстуры: каждый тест получает свежий объект, тесты не влияют друг на друга.

← К оглавлению урока