Философский вопрос про тестирование
От: vlp  
Дата: 16.01.21 01:04
Оценка: 6 (1)
Дано:

проект — над которым работает 15+ человек, состоящий из примерно десятка распределенных онлайн-сервисов, которые друг другу регулярно шлют по сети данные в своем формате.
(представьте что-то вроде, например, apache spark соединенных c hadoop, где-нибудь еще использующих еще redis — теперь все это своё собственное, заточенное под конкретную внутреннюю задачу по обработке бигдаты)

сервисы все написаны на одном языке, используют общие библиотеки, живут в монорепо, деплоятся раз в неделю.

каждый распределенный сервис состоит из большого количества классов сложной бизнес-логики, которая постоянно меняется, нетривиальным образом зависящих друг от друга. Сервисы сами по себе тоже нетривиальным образом зависят друг от друга и имеют API — контракты. "Нетривиальным" обзначает, что нет простого графа зависимостей (например, дерева), а он относительно "интересный", что-то вроде такого, причем в зависимостях могут быть и циклы.

проект развивался "органически", число классов и сервисов росло. Потихоньку приходит осознание, что то, что есть сейчас, потребность в тестировании покрывает недостаточно хорошо.

вопрос — как подходить к тестированию нового кода, чтобы не было мучительно больно и как жить дальше с увеличивающейся сложностью?

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

1. юнит-тесты: если есть класс A и он зависит от B, C и D, делаем моки B, С и D и тестируем класс A. Видим красивые цифры по 100% code coverage, пропускаем самые интересные случаи поведения D или C, которые забыли промокать. Недостаток — код хороших моков поддерживать дорого (особенно для классов бизнес-логики, которая часто меняется), плохие моки тестируют сферического коня в вакууме. Нельзя проверить ряд вещей, возникающих только под большой нагрузкой.

2. тяжелые интеграционные тесты: поднимают систему на локальной машине и/или в тестовом кластере и шлют тестовые данные через нее. Недостаток — тяжелые, медленные, неавтоматические, неудобно, плохо (или никак не) интегрируются с CI/CD пайплайном.

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

Чего хочется — что-то среднее между 1 и 2, чтобы тестировать группы классов сразу, но не поднимать для этого тяжелую систему целиком. Для этого, видимо требуется какой-то способ дизайна, отделяющий "тяжелые" классы от "легких" и использование самих классов вместо моков. Нутром я понимаю, как это делать, но хочется теоретической основы, чтобы вразумлять джуниоров. Пытался найти, как это называется, но пока не нашел.

Может, кто-нибудь умные статьи, книги или блоги посоветует или поделится своим релеватным опытом?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.