Найти тему
Колыбель приложений

Как перестать бояться и начать публиковать в продакшн

В этой статье мы рассказывали о реализации новой функции в приложении "Бюджет 2.0", о которой давно просили пользователи. А здесь мы рассмотрим техническую сторону вопроса, которая больше заинтересует разработчиков и тех, кто имеет отношение к продуктовой разработке в IT.

На первый взгляд задача не представляет собой ничего сложного: требуется создавать операции снятия денег с копилок, дублирующие обычные операции расходов, и в случае, если таких операций несколько, создавать вместо нескольких операций снятия с копилок одну общую.

Казалось бы, ну и чего здесь страшного? Пиши алгоритм и публикуй обновление! Но приложение считает деньги пользователей, и если накосячить в алгоритме и удалить десяток-другой операций с реальными кошельками, можно поиметь тонны негатива в отзывах и потерять драгоценные лояльность и пользовательскую оценку.

Unit-тесты как способ обезопасить свой код

Писать тесты для алгоритмов, работающих с базой данных в Android - одно удовольствие. Особенно, если для работы с базой вы используете Room, и у вас уже есть собственная реализация RoomDatabase. В этом случае для тестов вы просто создаёте экземпляр базы данных, которая хранится в оперативной памяти, и делаете с ней всё, что угодно.

  1. Инициализация экземпляра RoomDatabase, хранящейся в оперативной памяти и существующей только во время проведения тестов.
  2. Настройка имитации (mock) объекта SharedPreferences, который используется тестируемым алгоритмом, чтобы определить, включена ли у пользователя функция автоматического создания операций с копилками или же нет. В нашем случае всегда возвращает true.
  3. Вспомогательная функция для очистки содержимого базы данных между тестами.

Как только подготовительные работы для написания тестов завершены, можно приступать к самой интересной части - собственно написанию тестов! Не ограничивайте себя и дайте волю своей фантазии. Главная задача, которую вы можете решить с помощью Unit-тестов - это описать все так называемые крайние случаи и быть уверенным в работе своего алгоритма каждый раз при проведении рефакторинга или расширении существующего функционала.

Вот несколько примеров крайних случаев, в которых нам было необходимо гарантировать корректную работу алгоритма:

  • не пересоздавать заново уже существующую операцию снятия с копилки на требуемую сумму (потому что это привело бы к бесконечной пересылке данных между синхронизируемыми устройствами, если пользователь ведёт бюджет совместно с супругой/супругом)
  • удалять существующие операции снятия с копилок, если связанные с ними реальные операции расходов уже удалены
  • не снимать с копилки больше денег, чем в ней есть и т.д.

Вот пример одного из тестов, проверяющего работу алгоритма в случае, когда операция нужной категории произведена, а копилка, с которой можно снять деньги, ещё не создана:

-2
  1. Очистка тестовой базы данных и её наполнение исходными данными.
  2. Вызов функции, содержащей тестируемый алгоритм.
  3. Сверка фактических результатов работы алгоритма и наших ожиданий.

Итогом нашей работы над этим конкретным обновлением стал алгоритм, занимающий менее 100 строк кода, а также Unit-тесты практически на 1000 строк. Зато теперь при публикации обновления мы уверены, что не накосячили по крайней мере в самых очевидных вещах.

Когда смотришь как твой алгоритм проходит твои же тесты после итерации правок
Когда смотришь как твой алгоритм проходит твои же тесты после итерации правок

Кажется, что существует прямая зависимость между количеством написанных для кода тестов и уровнем душевного спокойствия разработчика. Кроме того, тренируясь писать Unit-тесты, вы развиваете в себе важнейшее для программиста умение - определять в собственных алгоритмах потенциальные дыры и ликвидировать их до тех пор, пока это не сделают тестировщики, менеджеры или, не дай бог, ваши пользователи.