ну давайте попробуем, не знаю как насколько это будет читабельно в текстовом виде.
первое с чего надо начать, что у нас как я уже говорил куча микросервисов. на самом деле их даже микросервисами назвать сложно, каждый из них довольно самостоятелен, за редким исключением что один сервис идет в другой.
почти весь рабочий флоу в проде строится на том, что сервисы по сути соеденены в цепочку. пришло событие на первый сервис, он его обработал и выплюнул результат куда либо (а базу, файл на диск, еще куда то).
этот результат ожидает второй сервис и тоже что то с ним делает, по пути выплевывая свой результат тоже куда либо. возможно для своей работы он разово сходит во другой сервис для какой то информации.
и так далее по цепочке. таких цепочек несколько, в них разные сервисы.
поднимать все это в целостную систему можно, но не имеет смысла, ибо довольно сложно будет организовать процесс тестирования, поэтому тестирование у нас исключительно модульное, то есть под каждый сервис свои тесты без участия других сервисов (за редким исключением, в некоторых случаях мы действительо тестируем связки, но это скорее исключение)
как я уже сказал тесты сами себе поднимают свой сервис, целиком его настраивают, готовят данные для тестов итд.
как это происходит? для начала рассмотрим запуск в 1 поток, без параллельности.
допустим мы запускаем тесты на 3 сервиса. у каждого сервиса есть отдельный класс, который описывает что это за сервис и что ему нужно для работы.
например первому сервису нужна база в mysql, база в кликхаусе, нужно 2 топика в кафке и ему нужна директория с файлами, к которой он сможет обращаться по http как к файловому серверу. пайтест на стадии инициализации видит что у нас будет тестироваться этот сервис и запускает этот класс.
класс знает имя сервиса, определяет номер потока пайтеста (в однопоточном режиме это 0) и составляет уникальный идентфиикатор этого сервиса (пусть будет service1_0). с этим идентификатором он создает рабочую директорию для сервиса, туда кладет его конфиг, при этом конфиг тоже генерируется под этот сервис и этот уникальный идентификатор, там же генерируется уникальный порт (или несколько), с которым сервис будет подниматься в будущем. с этим идентификатором в контейнере с базой создается база под этот сервис, эта же база прописывается в его конфиг. то же самое с КХ и кафкой. далее как я уже сказал если ему нужна какя то директория для файлов, он создает и ее внутри рабочей. на эту директорию он запускает nginx тоже на уникально сгенеренном порту, который будет файловым сервером. порт nginx тоже прописывается в конфиг сервиса.
после всех подобных операций у нас есть папка под конкретный сервис, уникально настроенный для будущей работы со всеми подготовленными глобальными зависимостями
то же самое происходит со всеми остальными сервисами.
сложность добавляет то, что просто так мы запустить сервис сразу нельзя, так как данные от будущих тестов должны уже быть во всех источниках (база, кх, кафка, диск, в моках итд) до старта сервиса, он читает их при старте разово, и не все сервисы потом могут в рантайме их дочитывать.
поэтому следующий этап - мы определяем все тесты которые собрались и будут запущены. каждый тест в классе, у каждого (почти каждого) есть метод prepare, который по сути и описывает как будут подготовлены данные для этого теста. если у теста удалось найти такой метод, на стадии завершения коллекта мы для каждого собранного теста гоним этот метод, при этом этот метод уже с помощью нехитрых манипуляций знает где в какой базе надо создать сущность, так как тест будет оноситься строго к своему сервису, а значит он знает его идентификатор и все выходящие. последовательно мы выполняем подготовку для каждого теста, а чтобы данные не пересекались - каждый тест генерирует данные с помощью отдельного класса - билдера, который использует последовательности. По сути итератор который ганартирует что если нам нужна какая то айдишка или еще что то - оно всегда будет новым и уникальным.
так как на стадии коллекта класс с тестом, в котором дергается метод подготовки это совсем не