В крупных проектах unit-тесты со временем превращаются в «тыкву»: их становится так много, что CI не выдерживает, а локальный запуск превращается в утопию. Ребята из Альфа-Банка столкнулись именно с этим в своем Android-приложении (800+ модулей) и поделились крутым кейсом оптимизации.
В чем была проблема?
1. Инициализация моков. Оказалось, что
mockk<Activity>() может занимать больше 1 секунды из-за генерации байт-кода на лету. Второго такого «золотого» вызова нет (кешируется), но...2. Gradle и изоляция. Gradle запускает тесты каждого модуля в отдельном JVM-процессе. Итог: кеш байт-кода не шерится, и в каждом модуле мы снова и снова тратим секунды на тяжелые моки.
3. Утечки в MockK. Метод
clearAllMocks() со временем работает всё медленнее из-за раздувания внутренней карты объектов.Что сделали:
✅ Отказались от стандартного распараллеливания Gradle по модулям. Вместо этого написали кастомную таску, которая собирает все тесты в единый classpath.
✅ Ручное дробление. Все тесты разбили на 10 чанков (по количеству ядер/воркеров) и запустили их в 10 долгоживущих JVM-процессах.
✅ Чистка рефлексией. Пофиксили деградацию MockK, принудительно очищая внутренние мапы библиотеки через рефлексию.
Результат:
Время прогона упало с 180 минут до 12 минут. На пулл-реквестах с учетом импакт-анализа тесты теперь пролетают вообще за минуту.
Минусы подхода:
Снижается изоляция. Если кто-то забыл сделать
unmockkStatic, это может «отстрелить» в совершенно другом тесте, который попал в тот же процесс.Подробный разбор с примерами кода и ссылкой на черновики Gradle-плагина читайте в статье: https://habr.com/ru/companies/alfa/articles/993352/
#Android #Gradle #Testing #Performance #Architecture
👉 @developer_mobila
