[Памятка: как делать правильные тесты]
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests