Ответ
unittest — это встроенный в Python фреймворк для модульного тестирования, являющийся частью стандартной библиотеки. pytest — популярный сторонний фреймворк, предлагающий более современный и гибкий подход к написанию тестов. Оба используются для написания и запуска тестов, но имеют существенные различия в синтаксисе, функциональности и экосистеме.
1. Синтаксис написания тестов
-
unittest: Требует создания классов, наследующихся отunittest.TestCase, и использования специальных методовassert*(например,assertEqual(),assertTrue()). Это может приводить к более многословному коду.import unittest class TestMath(unittest.TestCase): def test_addition(self): self.assertEqual(1 + 1, 2) def test_subtraction(self): self.assertTrue(5 - 3 == 2) -
pytest: Позволяет писать тесты как обычные функции или методы, используя стандартный операторassertPython. Это делает тесты более читаемыми, лаконичными и интуитивно понятными, так как не требует изучения специфических методов.# test_math.py def test_addition(): assert 1 + 1 == 2 def test_subtraction(): assert 5 - 3 == 2
2. Фикстуры (Fixtures)
-
unittest: Использует методыsetUp()иtearDown()(илиsetUpClass/tearDownClass) для подготовки и очистки тестового окружения. Они привязаны к жизненному циклу класса или метода и могут быть менее гибкими для переиспользования.import unittest class TestDatabase(unittest.TestCase): def setUp(self): # Подключение к БД перед каждым тестом self.db_conn = connect_to_db() def tearDown(self): # Закрытие соединения после каждого теста self.db_conn.close() def test_query(self): result = self.db_conn.execute("SELECT 1") self.assertEqual(result, 1) -
pytest: Предлагает более гибкую и мощную систему фикстур с помощью декоратора@pytest.fixture. Фикстуры могут быть функциями, возвращающими данные, и внедряться в тесты по имени. Это позволяет легко переиспользовать логику подготовки/очистки, управлять их областью видимости (функция, модуль, сессия) и создавать сложные зависимости.import pytest @pytest.fixture def db_connection(): # Подготовка ресурса (например, подключение к БД) conn = connect_to_db() yield conn # Тесты, использующие эту фикстуру, выполняются здесь # Очистка ресурса после выполнения тестов conn.close() def test_query(db_connection): result = db_connection.execute("SELECT 1") assert result == 1
3. Параметризация тестов
unittest: Параметризация сложнее, часто требует использованияsubTest(для Python 3.4+) или внешних библиотек, что может усложнить код.-
pytest: Встроенная и мощная поддержка параметризации с помощью декоратора@pytest.mark.parametrize, что позволяет легко запускать один и тот же тест с разными наборами входных данных, значительно сокращая дублирование кода.import pytest @pytest.mark.parametrize("a, b, expected", [ (1, 1, 2), (2, 3, 5), (0, 0, 0), ]) def test_addition(a, b, expected): assert a + b == expected
4. Отчеты и плагины
unittest: Генерирует базовые отчеты. Расширение функциональности часто требует написания кастомных тестовых раннеров.pytest: Генерирует более информативные и читаемые отчеты по умолчанию, включая подробную информацию о сбоях. Имеет богатую экосистему плагинов (например,pytest-covдля покрытия кода,pytest-xdistдля параллельного выполнения,pytest-mockдля моков), что значительно расширяет его возможности и гибкость.
Вывод
pytest часто предпочтительнее для новых проектов благодаря своей простоте, гибкости, мощной системе фикстур и обширной экосистеме плагинов, что способствует написанию более чистого и эффективного тестового кода. unittest остается надежным выбором для базового модульного тестирования, особенно в проектах, где он уже используется или когда требуется строгая совместимость со стандартной библиотекой.
Ответ 18+ 🔞
Ну слушай, тут тебе на пальцах объяснят, в чём разница между этими двумя штуками для проверки кода. Представь, что unittest — это как старый, добрый, но местами занудный дед, который всё делает по уставу. А pytest — это такой шустрый пацан, который пришёл и говорит: «Да зачем так сложно-то, можно же проще!».
1. Как тесты писать вообще
С unittest тебе надо, блядь, целый класс заводить, который от какого-то там TestCase наследуется. И вместо того чтобы просто написать assert 1+1 == 2, ты должен писать self.assertEqual(1+1, 2). Ну, ёпта, зачем столько букв? Это ж как на параде строем ходить.
import unittest
class TestMath(unittest.TestCase):
def test_addition(self):
self.assertEqual(1 + 1, 2) # О, боже, какой формализм!
А pytest — это просто праздник какой-то. Написал обычную функцию, внутри обычный assert, и всё, пошла писать губерния. Никаких лишних телодвижений.
def test_addition():
assert 1 + 1 == 2 # Всё, сука, элементарно. Никакой магии.
2. Фикстуры, или как подготовить всё для теста
В unittest для этого есть методы setUp и tearDown. Типа, перед каждым тестом ты что-то делаешь, а после — убираешь. Всё в рамках одного класса. Ну, работает, но если тебе эту подготовку в другом классе использовать — начинается геморрой.
import unittest
class TestDatabase(unittest.TestCase):
def setUp(self):
self.db_conn = connect_to_db() # Подключились
def tearDown(self):
self.db_conn.close() # Отключились
А pytest подходит к делу с умом. Ты создаёшь отдельную функцию, вешаешь на неё волшебный декоратор @pytest.fixture, и потом просто говоришь в тесте: «Дай-ка мне сюда эту фикстуру». И она тебе подсунуется. И самое охуенное — ты можешь сделать так, чтобы фикстура работала на всю сессию тестов, а не пересоздавалась каждый раз. Красота!
import pytest
@pytest.fixture
def db_connection():
conn = connect_to_db()
yield conn # Вот тут тесты выполняются
conn.close() # А вот тут всё прибирается
def test_query(db_connection): # Смотри-ка, фикстура сама пришла!
result = db_connection.execute("SELECT 1")
assert result == 1
3. Параметризация, или как одним выстрелом убить кучу зайцев
В unittest чтобы прогнать один тест с разными данными, надо либо городить циклы, либо использовать subTest. Не сказать чтобы очень удобно.
А pytest делает это одной строчкой. Навесил декоратор @pytest.mark.parametrize, перечислил варианты — и сиди, радуйся. Один тест, а проверяет кучу случаев.
import pytest
@pytest.mark.parametrize("a, b, expected", [
(1, 1, 2),
(2, 3, 5),
(0, 0, 0),
])
def test_addition(a, b, expected):
assert a + b == expected # И не надо три одинаковых функции писать!
4. Отчёты и разные плюшки
unittest выдаёт отчёт, ну... как умеет. Сухо, информативно, но без изысков.
А pytest, ёпта, когда тест падает, так красиво всё распишет, что иногда даже смешно становится. Плюс к нему есть овердохуища плагинов: для проверки покрытия кода, для параллельного запуска, для создания моков. Хочешь — в облако результаты отправляй.
Итог, блядь
Если у тебя старый проект на unittest и всё работает — можешь не париться. Но если начинаешь что-то новое или хочешь, чтобы тесты писались быстро и читались легко — бери pytest. Он просто берёт и делает сложные вещи простыми, а это, в рот меня чих-пых, дорогого стоит.