Практическое покрытие сервиса тестами

Инструменты и фреймворки

Автоматизация тестирования
Интеграционное тестирование
Юнит-тестирование
Профилирование и отладка кода

Доклад отозван

Целевая аудитория

Golang разработчики, которые не смотрели на юнит тесты дальше stdlib и testify.

Тезисы

В прошлом году мы рассказали о том, как переписали сервис поиска с python+django+postgres на свой in memory cache на базе golang + postgres (https://www.youtube.com/watch?v=HStcpNDCwGo). Но написать код — это полдела; теперь нужно гарантировать его развитие без страха сломать продакшен. В этом докладе мы обсудим, как «зацементировать» сложную логику тестами.

Мы пойдем против классических канонов: разберем, почему честные unit-тесты иногда обязаны ходить в базу данных и как сделать это удобно. Разберём хватит ли для организации этого подхода stdlib в Go или нужны какие-то дополнительные библиотеки. А также составим список “слепых” зон (вроде метрик), которые обычно не покрываются никакими тестами в типовых golang + postgresql сервисах.

## План

* Интро (1м)
* С чем уйдём с доклада (2м)
* Как надо покрывать
* В каких случаях
* Recap сервиса поиска (2м)
* Показать что это golang+postgresql с типовым стеком (go-pg, gRPC, prometheus)
* Указать слои: engine \-\> memory \+ postgresq
* Обозначить проблему переезда
* Что нам нужно? (1м)
* Иметь базу (cold storage) как финальную проверку корректности
* Проверять консистетность памяти и базы
* Убедиться что полный цикл поведения верный
* Чуток философии про тестирование (2м)
* Немного воды про то, что есть пирамида тестирования и вот в ней говориться что кост на юниты самый дешёвый, а на e2e самый дорогой.
* Указать что кроме пирамиды есть ещё и сота/honeycomb тестирования, где большую роль отдают интеграционным тестам.
* Обозначить что споры где заканчивается юнит: там где всё замокано или где что-то в оунершипе сервиса (кеш, база) должно использоваться той же версии.
* Высказать своё мнение о том, что тестирование должно быть близко к продакшен сетапу и в нашем случае база (кроме версий) вряд ли будет меняться, поэтому надёжнее с ней.
* Тестирование базы в юнитах (5м)
* Удобство генерации данных \- в нашем сервисе есть связка отель-\>комната-\>тариф-\>дата и чтобы тесты полностью прошли надо это всё уметь генерировать. При этом важно ещё соблюсти зависимости этих генераций \- тарифа без комнаты не бывает. В динамических языках (python/ruby) есть решения типа factory boy, в golang же \- это приходится писать руками. Показать что это не занимает так долго времени, показать что это тоже надо покрыть и как после этого выглядят тесты.
* Работа с параллельными тестами при базе \- сейчас в коммунити есть мнение про testcontainers, что если уж выбрали базу то вот поднимайте её на каждый чих что будет гарантировать отсутствие пересечений. Это слишком замедляет тесты, и ответ на это простой: использовать транзакции. Покажу agnostic helper для go-pg \+ stdlib.
* Мы сказали о том, что база последний бастион корректности данных поэтому часть логики в триггерах и констрейнтах. Из-за этого будут слайды про то, что это тоже надо тестировать.
* Ещё у нас была команда для переноса данных и такое обычно тоже тестами не покрывают, но только благодаря этому словили баг в 16 постгресе ([https://www.postgresql.org/message-id/18130-7a86a7356a75209d@postgresql.org](https://www.postgresql.org/message-id/18130-7a86a7356a75209d@postgresql.org) )
* Тестирование параллельного кода (5м)
* Рассказать про synctest.Test из go 1.24, который помог нам протестировать свою реализацию circuit breaker. Здесь можно чуток пойти вглубь как golang это реализует.
* Стейт в памяти \+ типа cron job. Показать типовую синхронизацию через каналы (по аналогии с graceful shutdown), что для тестов можно на вход передавать канал и потом его дожидаться.
* Второе \- проверка консистентности между записью в память и в базу. Здесь вообще о том, что это поведение можно сделать отдельным тулом. Делаем внутренний механизм снапшотов, стреляем в сгенерироваными данными и сверяем что снапшоты совпадают логически. Упомянуть что по хорошему такое надо делать на jepsen.
* Проверяем поведение BDD (3м)
* Переходим к тому что в сервисе куча комбинаторики, но при этом описывается внешне он достаточно понятен в входных и выходных данных
* Сгружаем проверку в QA на написание сценариев на языке геркин
* Реализуем это в Golang через godog: тут больше про то как это сетапить, повторяем про генерацию данных из базы, показываем код как менеджить контекст BDD тестов
* В целом, скорее даже вывод о том что часть хелперов для юнитов отлично переиспользуется на этом уровне
* Ничего не забыли протестировать? (2м)
* Метрики / Прометей
* Логи
* Алерты
* Выводы (1м)
* Не мокайте базу
* Думайте о поведение всей системы
* Строчки кода для обсервабилити \- это тоже часть системы

Иван Чернов

Островок!

Системный архитектор.

https://vanadium23.me/about/

Видео