⚖️ Старый vs Новый: подходы к unit-тестированию

О странице: Здесь собраны устаревшие паттерны из лекции и их современные аналоги. В основной теории используются только актуальные подходы.

1. Ручной assert (из лекции) vs pytest

Из лекции (старое)Современный подход (pytest)
Ручная проверка через assert в скрипте Тест-функция с именем test_*, запускаемая автоматически
При падении одного assert скрипт ломается — остальные не запускаются pytest запускает все тесты независимо друг от друга
Нет отчёта — только вывод в консоль Подробный отчёт с информацией о каждом тесте

Из лекции (старое) — ручной assert

# calculator_test.py (из лекции — ручной запуск)
from calculator import Calculator

calculator = Calculator()

# Тест 1: положительные числа
res = calculator.sum(4, 5)
assert res == 9

# Тест 2: отрицательные числа
res = calculator.sum(-6, -10)
assert res == -16

# Тест 3: деление на ноль
try:
    calculator.div(10, 0)
except ArithmeticError as e:
    assert str(e) == "На ноль делить нельзя"

Современный подход — pytest с фикстурой

# test_calculator.py (современный pytest)
import pytest
from calculator import Calculator

@pytest.fixture
def calculator():
    return Calculator()

def test_sum_positive_numbers(calculator):
    assert calculator.sum(4, 5) == 9

def test_sum_negative_numbers(calculator):
    assert calculator.sum(-6, -10) == -16

def test_division_by_zero(calculator):
    with pytest.raises(ArithmeticError, match="На ноль делить нельзя"):
        calculator.div(10, 0)

2. unittest.TestCase (старый стандарт) vs pytest

unittest.TestCasepytest
Нужно наследоваться от TestCaseОбычные функции
Методы: assertEqual, assertRaises и т.д.Обычный assert
setUp / tearDown@pytest.fixture
Входит в стандартную библиотеку PythonНужна установка через pip

unittest.TestCase (из лекции — встречается в старых проектах)

# test_calculator_unittest.py (стиль unittest)
import unittest
from calculator import Calculator

class TestCalculator(unittest.TestCase):
    def setUp(self):
        self.calculator = Calculator()

    def test_sum_positive(self):
        self.assertEqual(self.calculator.sum(4, 5), 9)

    def test_sum_negative(self):
        self.assertEqual(self.calculator.sum(-6, -10), -16)

    def test_division_by_zero(self):
        with self.assertRaises(ArithmeticError):
            self.calculator.div(10, 0)

if __name__ == '__main__':
    unittest.main()

pytest (современный, предпочтительный)

# test_calculator.py (pytest — читается проще)
import pytest
from calculator import Calculator

@pytest.fixture
def calculator():
    return Calculator()

def test_sum_positive(calculator):
    assert calculator.sum(4, 5) == 9

def test_sum_negative(calculator):
    assert calculator.sum(-6, -10) == -16

def test_division_by_zero(calculator):
    with pytest.raises(ArithmeticError):
        calculator.div(10, 0)

3. Параметризация: дублирование кода vs @pytest.mark.parametrize

Старый подход (дублирование)

# Дублирование — 5 отдельных тестов с одинаковой логикой
def test_sum_case1(calculator):
    assert calculator.sum(4, 5) == 9

def test_sum_case2(calculator):
    assert calculator.sum(-6, -10) == -16

def test_sum_case3(calculator):
    assert calculator.sum(-6, 6) == 0

def test_sum_case4(calculator):
    assert round(calculator.sum(5.61, 4.29), 1) == 9.9

def test_sum_case5(calculator):
    assert calculator.sum(10, 0) == 10

Современный подход (параметризация)

# Один тест покрывает все случаи
@pytest.mark.parametrize('num1, num2, result', [
    (4, 5, 9),
    (-6, -10, -16),
    (-6, 6, 0),
    (5.61, 4.29, 9.9),
    (10, 0, 10)
])
def test_sum(num1, num2, result):
    calculator = Calculator()
    res = calculator.sum(num1, num2)
    assert res == result