Ответ
F.I.R.S.T. — это акроним, который описывает набор принципов для написания качественных и эффективных юнит-тестов. Следование этим принципам помогает создавать надежный и поддерживаемый тестовый набор.
- Fast (Быстрые): Тесты должны выполняться очень быстро. Медленные тесты тормозят разработку, и их реже запускают, что снижает их ценность.
- Isolated / Independent (Изолированные / Независимые): Тесты не должны зависеть друг от друга. Порядок их запуска не должен влиять на результат. Каждый тест должен настраивать и очищать свое собственное окружение.
- Repeatable (Повторяемые): Тест должен давать одинаковый результат при каждом запуске в любой среде (локальная машина, CI/CD-сервер). Это исключает зависимость от внешних факторов, таких как сеть или база данных.
- Self-Validating (Самопроверяемые): Результат теста должен быть однозначным: либо
pass, либоfail. Он не должен требовать ручной проверки логов или сравнения файлов. - Timely (Своевременные): Тесты следует писать своевременно, в идеале — непосредственно перед кодом, который они тестируют (подход Test-Driven Development, TDD). Это помогает лучше спроектировать код.
Пример, демонстрирующий изоляцию (Python + unittest.mock):
import unittest
from unittest.mock import Mock
# Класс, который мы хотим протестировать
class ReportGenerator:
def __init__(self, db_connector):
self._db = db_connector
def generate_daily_report(self):
# Получает данные из "базы данных"
user_count = self._db.get_active_users_count()
if user_count > 100:
return "High activity report"
return "Normal activity report"
# Тест
class TestReportGenerator(unittest.TestCase):
def test_high_activity_report(self):
# Arrange: Создаем mock-объект для зависимости
mock_db = Mock()
# Настраиваем его поведение
mock_db.get_active_users_count.return_value = 150
# Act: Создаем экземпляр с mock-объектом
generator = ReportGenerator(db_connector=mock_db)
report = generator.generate_daily_report()
# Assert: Проверяем результат (Self-Validating)
self.assertEqual(report, "High activity report")
# Этот тест:
# - Fast: не делает реального запроса к БД.
# - Isolated/Repeatable: не зависит от состояния реальной БД. Ответ 18+ 🔞
А, ну ты глянь, какие принципы придумали, чтобы тесты не превращались в ебучую кашу, которую потом сам же и разгребаешь с утра до вечера, блядь. F.I.R.S.T., говоришь? Ёпта, звучит как заклинание, но сейчас разберемся, что за хитрая жопа тут скрывается.
F — Fast (Быстрые). Тесты должны бегать как угорелые, а не ползти как черепаха в сиропе. Если запускаешь их и успеваешь чай заварить, сходить покурить и вернуться — это пиздец, а не тесты. Никто их запускать не будет, и смысл тогда?
I — Isolated / Independent (Изолированные / Независимые). Это, сука, святое. Каждый тест — как отшельник в своей пещере. Он нихуя не должен знать о соседях. Запустил их вразнобой, всё равно должны работать. Если один тест ломает другой — это не тесты, а мартышлюшка с гранатой.
R — Repeatable (Повторяемые). Запустил на своей тачке, запустил на сервере у сисадмина — результат один и тот же, пизда. Никаких «а у меня работает», «а сегодня среда, поэтому падает». Если тест зависит от погоды на Марсе — это хуйня, а не тест.
S — Self-Validating (Самопроверяемые). Тут всё просто, как три копейки. Запустил — увидел зеленую галочку или красный крестик. Не надо вручную глазами пялиться в логи, сравнивать JSON-ки или гадать на кофейной гуще. Либо «да», либо «нет», либо «иди нахуй, тут всё сломалось».
T — Timely (Своевременные). Лучший момент написать тест — это прямо перед тем, как пишешь сам код. А ещё лучше — вообще до него, эта ваша TDD-магия. Так и голова лучше работает, и код получается менее кривой, потому что ты сразу думаешь, как его потом протестировать, а не как от него отвертеться.
Вот, смотри, пример на питоне, чтобы было понятно, про что это я тут распинаюсь. Особенно про изоляцию — это когда ты вместо реальной базы данных подсовываешь какую-нибудь заглушку, которая делает вид, что она база, а на самом деле она просто муляж, блядь.
import unittest
from unittest.mock import Mock
# Класс, который мы хотим протестировать
class ReportGenerator:
def __init__(self, db_connector):
self._db = db_connector
def generate_daily_report(self):
# Получает данные из "базы данных"
user_count = self._db.get_active_users_count()
if user_count > 100:
return "High activity report"
return "Normal activity report"
# Тест
class TestReportGenerator(unittest.TestCase):
def test_high_activity_report(self):
# Arrange: Создаем mock-объект для зависимости
mock_db = Mock()
# Настраиваем его поведение
mock_db.get_active_users_count.return_value = 150
# Act: Создаем экземпляр с mock-объектом
generator = ReportGenerator(db_connector=mock_db)
report = generator.generate_daily_report()
# Assert: Проверяем результат (Self-Validating)
self.assertEqual(report, "High activity report")
# Этот тест:
# - Fast: не делает реального запроса к БД.
# - Isolated/Repeatable: не зависит от состояния реальной БД.
Видишь? Мы тут не лезем в настоящую базу, которая может сегодня лежать, а завтра — нет. Мы сказали этой заглушке: «Слушай сюда, муляж, когда у тебя спросят количество юзеров, ты говори — 150». И всё, тест летает, повторяется и ни от кого не зависит. Красота, ёпта! Вот так и надо делать, а не городить огород из костылей и надежд на авось.