Рассказ о том, как перестать откладывать на потом скучные, но важные вещи.
Контекст
Была у меня задача - проверять, является ли клиент крупным, или нет. Фактически, надо было, если определенная строка равна “крупный” (в любом регистре), то вернуть “Y”, иначе “N”, и записать это в базу, как bool
.
Казалось бы, элементарщина.
Ну, я и набросал простую лямбду:
lambda x: "Y" if x.lower == "крупный" else "N",
Посмотрите в этот код десять секунд, всё же нормально?
Потому что, когда код попал в продакшн, начались странности. Все записи были промечены как “мелкие”, true
нигде не проставлялось, хотя должно было. По итогу, я потратил несколько часов, разбираясь, откуда растут ноги, и, в итоге, заметил, что:
.lower
— это я указываю на метод, а не вызываю его! 😥
Корректный код выглядел бы так:
lambda x: "Y" if x.lower() == "крупный" else "N",
Как обычно, небольшая опечатка — и уже всё работает не так, как задумано, а инженер часами пялит в монитор.
Справедливости ради, конкретно тут львиная доля проблемы была в адово долгом feedback loop - это “проект выходного дня”, где используются очень скромные мощности, решение собирается в docker-образ и доставляется в закрытый контур через scp
по каналу ограниченной пропускной способности.. Но это всё лирика, оправдания есть всегда.
Что меня спасло бы?
Достаточно было добавить несколько простейших тестов (хоть doctest):
>>> check_size("Крупный")
"Y"
>>> check_size("крупный")
"Y"
>>> check_size("иное")
"N"
Если бы я их написал сразу, то и ошибка всплыла бы мгновенно, а не после того, как эта лямбда уже была спрятана в глубинах логики, а автор стал рассчитывать на неё в production-окружении.
Я и раньше знал, что тесты — это полезно. Но как-то внутренне осознал и зауважал их ценность только после этого случая.
Даже на такую логику “в одну строчку”.
Подытожим
- Глаз замыливается, опечатки неизбежны
- Тесты экономят время - лучше потратить 5 минут на проверку, чем часы на дебаг
- Shift left - если что-то сломается в будущем, вы сразу это заметите
Ссылки
Проконтролировать себя поможет CI/CD, куда можно встроить запрет выкатки без достаточного покрытия тестами:
- coverage - модуль для проверки покрытия тестами
- codeclimate - сервис с бесплатным тиром, который легко подключить для pet-проекта