🐛 Типичные ошибки при unit-тестировании

⚡ Топ-3 ошибки

  1. Нет префикса test_ — pytest не найдёт функцию.
  2. Сравнение float с == — используйте round() или pytest.approx().
  3. Общее состояние между тестами — создавайте объект в фикстуре, а не на уровне модуля.

Ошибка 1: Отсутствие префикса test_

# НЕПРАВИЛЬНО — pytest проигнорирует эту функцию
def check_sum():
    calculator = Calculator()
    assert calculator.sum(4, 5) == 9

def sumTest():           # тоже не найдёт
    assert True

# ПРАВИЛЬНО
def test_sum():
    calculator = Calculator()
    assert calculator.sum(4, 5) == 9
Симптом: collected 0 items — pytest собрал ноль тестов, хотя функции есть. Проверьте имена файла и функций.

Ошибка 2: Сравнение float с оператором ==

# НЕПРАВИЛЬНО — может упасть из-за погрешности float
def test_sum_floats(calculator):
    result = calculator.sum(5.6, 4.3)
    assert result == 9.9   # AssertionError! (9.8999999... != 9.9)

# ПРАВИЛЬНО — вариант 1: round()
def test_sum_floats(calculator):
    result = calculator.sum(5.6, 4.3)
    assert round(result, 1) == 9.9

# ПРАВИЛЬНО — вариант 2: pytest.approx (рекомендуется)
def test_sum_floats(calculator):
    result = calculator.sum(5.6, 4.3)
    assert result == pytest.approx(9.9, rel=1e-1)

Ошибка 3: Общее (изменяемое) состояние между тестами

# НЕПРАВИЛЬНО — один объект используется во всех тестах
# Если test_A изменит состояние calculator — test_B получит "грязный" объект
calculator = Calculator()   # на уровне модуля

def test_A():
    assert calculator.sum(1, 2) == 3   # ok

def test_B():
    # если Calculator хранил бы историю операций — тест мог бы получить
    # не то, что ожидает
    assert calculator.div(10, 2) == 5

# ПРАВИЛЬНО — фикстура создаёт свежий объект для каждого теста
@pytest.fixture
def calculator():
    return Calculator()

def test_A(calculator):
    assert calculator.sum(1, 2) == 3

def test_B(calculator):
    assert calculator.div(10, 2) == 5

Ошибка 4: Тест не проверяет исключение правильно

# НЕПРАВИЛЬНО — исключение перехватывается, но тест проходит даже без ошибки
def test_division_by_zero_bad(calculator):
    try:
        calculator.div(10, 0)
    except ArithmeticError:
        pass   # Тест проходит всегда! Даже если исключение не поднялось

# ПРАВИЛЬНО — используйте pytest.raises()
def test_division_by_zero(calculator):
    with pytest.raises(ArithmeticError, match="На ноль делить нельзя"):
        calculator.div(10, 0)
# Если исключение НЕ поднято — тест упадёт с Failed (pytest.raises гарантирует это)

Ошибка 5: Предупреждения о незарегистрированных маркерах

# Вы написали:
@pytest.mark.positive_test
def test_sum(calculator):
    assert calculator.sum(4, 5) == 9

# pytest выдаёт:
# PytestUnknownMarkWarning: Unknown pytest.mark.positive_test
# - did you mean: ...?

# РЕШЕНИЕ: создайте pytest.ini и зарегистрируйте маркер
# pytest.ini
[pytest]
markers =
    positive_test: Тесты для позитивных сценариев

Ошибка 6: Неправильное использование skipif — строка вместо условия

# НЕПРАВИЛЬНО в современном pytest — строковое условие устарело и ведёт себя
# не так, как ожидается
@pytest.mark.skipif("sys.version_info < (3, 8)", reason="...")
def test_something():
    pass

# ПРАВИЛЬНО — передать логическое выражение напрямую
import sys

@pytest.mark.skipif(sys.version_info < (3, 8), reason="Требуется Python 3.8+")
def test_something():
    pass
В лекции показан устаревший строковый вариант condition="sys.version_info < (3, 8)". Начиная с pytest 7+ рекомендуется передавать выражение напрямую без кавычек.

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