Всем привет.
Меня зовут Евгений Фроликов я разработчик в АльфаСтрахование
В ходе работы над проектом в АльфаСтрахование пишем проект на микро сервисах и сложилось так что один из «микро сервисов» сильно разросся (но до монолита ему ещё далеко 🙂 ). Жили мы так долго и счастливо , пока не стали «переезжать» в облако, тут и начались приключения.
Переезд не чем особенно не запомнился для команды разработки только вопросами от DevOps насчёт портов и т.д. Замечу что все интеграционные тесты мы выпилили для того чтобы отвязаться от зависимости от других команд когда у них что то падает на тестовых стендах. Но стало происходить «магия» в JUnit тестах , а именно стали падать тесты. Падали они фантомное и не предсказуемо , лечилось до поры до времени это retraem pipeline , до тех пор пока эта проблема не стала блокером для выкладок изменений .

Дальше просто retraem

И так можно было «крутить рулетку» долго и упорно.
Стали разбираться (спасибо понимающему бизнесу за то что дали на это время) . Так примерно выглядели заголовки наших тестов (которых было ооочень много , так как мы иcпользуем Sonar).
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class ContractStatusServiceTest { @Autowired private ContractStatusService contractStatusService; @MockBean private RsaInfoComponent rsaInfoComponent; @MockBean private ContractRepository contractRepository;
Давайте разберём «магические» анотации
1)@RunWith(SpringJUnit4ClassRunner.class) —Запуск контейнера Spring для выполнения модульного теста
2)@SpringBootTest -аннотация говорит Spring Boot пойти и найти основной класс конфигурации (например, с @SpringBootApplication) и использовать его для запуска контекста приложения Spring. SpringBootTest загружает полное приложение
3)@Autowired — Инжектит Bean;
И закралась мысль а почему мы используем @Autowired ведь его строго не рекомендуют использовать при разработке приложений , так как есть риск что он не успеет проинжектиться в поле.
Начались эксперименты такова рода.
@RunWith(SpringRunner.class) @SpringBootTest @RequiredArgsConstructor public class ComponentTestTest { // @Autowired private final ComponentTest componentTest;
То есть попытка проинжектить бин как в приложение , через конструктор
1)@RequiredArgsConstructor — Аннотация Lombok для автоматического создания конструкторов из полей final.
Но…..
java.lang.Exception: Test class should have exactly one public zero-argument constructor at org.junit.runners.BlockJUnit4ClassRunner.validateZeroArgConstructor(BlockJUnit4ClassRunner.java:171) at org.junit.runners.BlockJUnit4ClassRunner.validateConstructor(BlockJUnit4ClassRunner.java:148) at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:127) ...
а жаль.
Дальше стали приходить осознавание , а зачем мы поднимаем контекст всего приложения для простых тестов и вспомнили про Mock
@RunWith(MockitoJUnitRunner.class) public class CrossProductServiceTest { @InjectMocks private CrossProductService crossProductService; @Mock private KaskoService kaskoService; @Mock private CrownVirusOfferService crownVirusOfferService;
Давайте разберёмся что тут происходит и всём принципиальная разница
1)@RunWith(MockitoJUnitRunner.class) — Заполняет заглушками наш Bean , а не поднимается контекст (подробнее можно почитать в доках )
2)@Mock — сама заглушка
3)@InjectMocks — создаёт Bean и передаёт в конструктор заглушки
И всё «звалось»))
Плюсы :
1)У нас в разы ускорились тесты при деплоях (так как контекст не поднимается)
2)Мы перестали ловить и бояться не проинжектеных бинов
Минусы:
1)Пришлось переписывать все тесты
ссылка на оригинал статьи https://habr.com/ru/company/alfastrah/blog/541432/
Добавить комментарий