Комментарии 20
Если нужно тестировать два микросервиса в связке, но не систему/подсистему целиком, то может лучше было делать один микросервис?
Да, если какая-то логика распределена по подсистеме, то лучше (а на каком-то уровне тестирования даже необходимо) тестировать её целиком. При этом в этой подсистеме может быть разное количество сервисов. Если количество сервисов больше одного, то можно применить подход, описанный в статье.
Причин же разбить систему на микросервисы существует много и это отдельная архитектурная задача. Здесь и разделение логики, и разные деплойменты, и изоляция ресурсов и многое другое.
Звучит опасно - сначала тестируем конкретную связку, а потом "разный деплой".
В целом я не к тому что это ужас-ужас так делать, но на мой взгляд это тревожный звонок, если приходится выделять какое-то подмножество микросервисов из подсистемы и отдельно с ним работать.
Кажется, я Вас не понимаю. Можете раскрыть подробнее, что кажется вам опасным?
То что вы сильно связываете два микросервиса внутри системы/подсистемы, создавая еще один уровень абстракции/управления между микросервисом и системой/подсистемой. В общем случае так делать плохо с архитектурной точки зрения.
Понял.
Я не предлагаю выдирать микросервисы из подсистемы. Если ваша подсистема включает, скажем, десять микросервисов - так и тестируйте десять микросервисов. В предлагаемом подходе нигде нет ограничений на количество (кроме ресурсов машины, на которой исполняете).
В целом этот подход ничем не отличается от обычных тестов на подсистему, но дает два преимущества: проще запускать из ide при разработке в рамках TDD + возможность прогонять тесты на этапе пулл-реквеста, не деплоя на энв.
Хорошая архитектура в глобальном смысле - не только о возможностях, но и об ограничениях и посылах.
Тут вы даете возможность, но что насчет ограничений?
Давайте приведу вот еще такой пример: цель - деплоить несколько раз в день. Не в конце спринта, если у вас Скрам, а вот реально несколько раз в день. Одна эта цель меняет все - от стиля написания кода и размеров pull request'ов, до способа управления проектом (Скрам уже не подходит).
Автор, ваш подход, наверное, будет работать на вашем конеретном проекте, но читателей я прошу до последнего так не делать.
Лично в моейм опыте во всех проектах, где требовалось тестирование сервисов в связке, этим решали не причину, а следствие. Причина же была одновременно простая, но сложно исправляемая - ошибки в архитектуре.
Вы выше упоминаете независимый деплой. На мой взгляд, независимый деплой возможен только при независимой разработке. У меня есть определенные подозрения, что в данном случае с независимой разработкой так себе. Я не авторитет? Без проблем, давайте посмотрим что говорят Sam Newman и Martin Fowler: https://youtu.be/GBTdnfD6s5Q
К чему подобные тесты приводят в long-term: нестабильные тесты, огромные затраты на поиск причины свалившегося теста и изменение кучи тестов при выкатывании новых фич. Надеюсь, у вас получается этого избежать.
Простите, не понимаю. Вы против сквозного тестирования? Вы против интеграционного тестирования? Вы против того, чтобы один микросервис обращался к другому?
Если ответ на последний вопрос "нет, не против", то как и в какой момент можно убедиться, что все правильно работает?
Понимаете, способов обеспечить качество много и парой видов тестов все не ограничивается.
Я за то, чтобы воспринимать другой (микро)сервис как совершенно внешний, который делает другая компания со своим релизным циклом, на незнакомым вам языке программирования и доступа к исходникам у вас нет. Стремление к этому и делает деплой/разработку по настоящему независимым. Вот что вы тогда будете делать?
Ответив на вопрос выше уже можно думать как в вашем конкретном случае проверять систему. И мой ответ будет зависеть, прежде всего, не от архитектуры, а от бизнес-контекста и людских ресурсов.
На нескольких проектах я задавал такой вопрос. У нас есть отдельно протестированный сервис А и отдельно протестированный сервис B, где взаимодействие с сервисом A замокано. Зачем нам тестировать еще и end-to-end сценарии, если есть строгие контракты? Что мы тестируем вообще? Бизнес логика протестирована изолировано. Конфигурацию и инфраструктуру? Так это не сквозным тестированием проверить можно. Тогда что? Просто повторим наши юнит тесты? Смысл? Четстного ответа "мы не доверяем разработчикам" я так и не дожидался, хотя по косвенным признакам было понятно, что проблема где-то тут.
А вот по Testcontainers для баз, например, я полностью поддерживаю, кстати.
Спасибо, понятно. Тут мне возразить нечего: если все возможное поведение всех микросервисов описано, одинаково понято и полностью покрыто юнит-тестами, то интеграционное тестирование функциональности не нужно.
Но да, я не доверяю разработчикам. Я сам разработчик и я себе не доверяю. Я подозреваю себя в том, что:
я плохо понял контракт
я недостаточно покрыл тестами граничные условия, а по границам может пролегать success story
мокая другой сервис, я не включил в ответы все возможные значения
Поэтому я успокаиваю себя тем, что добавляю сквозные тесты, которые покрывают хотя бы главные успешные сценарии и очевидные негативные.
Понимаете какая штука... А вам точно нужно протестировать вот прям все сценарии с граничными условиями?
Ресурсы же не бесконечны и очень часто приходится расставлять приоритеты и чем-то жертвовать. Настолько ли ваша система критична, что даже час-два, к примеру, простоя вызовет огромные денежные потери для компании? Точно ли вам надо тратить пару человеко-месяцев на разработку и отладку такого решения, а не фичи пилить?
И проблема-то не только в единожды затраченом времени. Проблема еще и в том, что это все надо поддерживать, рассказывать новичкам, ревьюить, чинить нестабильные тесты (а они будут), иметь долгое время сборки, баги будут чиниться намного сложнее и дольше, чем при помощи юнит тестов и т.п. А, кроме того, наличие такого решения будет побуждать им пользоваться, писать еще больше интеграционных тестов и постепенно усугублять проблемы.
Как инженер, я вас полностью понимаю. Из кучи разных библиотек и технологий создать достаточно элегантное решение, продравшись через огромное количество мелких и не очень технических проблем. Я очень хорошо знаю это чувство радости, когда оно все начинает работать.
Проблема в том, что, возможно (!), вы чинили не причину, а следствие. Отсутствие доверия, в т.ч. и к самому себе, - это такая же проблема, как упавший тест или баг в продакшене. И ровно так же эта проблема может быть решена. Только смотреть тут надо на коммуникацию, процессы, архитектуру, метрики качества, насколько детальный анализ причин каждого бага проводится и т.д. Для инженера это сложно и непривычно, знаю по себе.
А вам точно нужно протестировать вот прям все сценарии с граничными условиями?
Как раз нет. Я как раз про то, что со сквозными тестами нет нужды проверять все. А вот с юнит-тестами и контрактом как раз напротив. Если "воспринимать другой (микро)сервис как совершенно внешний", то и ожидать от него можно все, что угодно. У юнит-тестов плюс - быстрое исполнение - это Вы верно заметили. Но как понять, что он проверяет сценарий, которые случается и как понять, что сценарии, которые случаются - покрыты тестами?
наличие такого решения будет побуждать им пользоваться, писать еще больше интеграционных тестов и постепенно усугублять проблемы.
Да. Я сделал решение, чтобы им пользовались. Это не серебрянная пуля. Она позволяет махом проверить систему, но у нее есть цена. Для проверки одного микросервиса есть, например, @SpringBootTest
, для проверки класса или функции - достаточно обычных тестов. У каждого типа - своя ниша и своя цена.
Мне кажется, что мы перешли от обсуждения конкретного решения к обсуждению, нужны ли end-to-end тесты вообще. Это так?
Ожидать от внешнего сервиса можно что угодно, да. И то, что контракт может не исполняться. Проблема в том, что есть вещи в зоне нашей отвественности, а есть те, о которых мы должны знать (что они могут происходить), но вне нашей зоне ответственности. Вот оказалась на сервере битая плата оперативы. Вы это явно не тестами проверять будете, особенно если в облаке хоститесь. Соответственно, вкладываясь в интеграционные тесты, вы не вкладываетесь в observability и укорачавание релизного цикла (ресуры не бесконечны).
Как проверить какие сценарии покрыты тестами, а какие - нет? В общем случае гарантировать это невозможно. На уровне юнит тестов - code review. На уровне системы - а точно надо? Но даже если и надо, то, имхо, первичный вопрос - что делать с багами, найденными там? Я бы каждый найденный там баг спускал бы в команду разработки с вопросом как починить причину его возникновения (чек листы на code review, рефакторинг, архитектура, тренинги, ...) А вопрос как тестировать end-to-end для меня вторичен. Да хоть руками, если это экономически выгоднее.
Проблема в том, что качество не мерится только лишь процентом покрытия. На одном из проектов я вот прям и говорил: граничные условия покрываем только если есть свободное время. Это немного утрированно, но посыл был примерно такой, о приоритетах. Тем не менее, проблем с качеством было крайне мало и 90% багов было связано с плохим пониманием контрактов, что надо было решать не тестами, а привлечением аналитика. Ресурсов на аналитика не было, тч потерпели первые пару месяцев в продакшене, тестируя руками, дальше полет нормальный, все довольны. Но, да, мы использовали строгие контракты везде, где можно, много кодогенерации, задали высокую планку для code review, у нас в каждый pull request автоматом отправлялся check list, а также писали кучу правил для статического анализа (см ArchUnit).
Плюс юнит тестов не сколько в быстром исполнении, сколько в локализованности - вы очень быстро найдете где конкретно падает. Чем выше мы поднимаемся по пирамиде тестирования, тем дольше это время. На нынешнем проекте я месяца полтора убеждал заказчика писать меньше интеграционных тестов и больше юнит - команда разработки тратила 10-15% времени на починку и разбор нестабильных интеграционных тестов. Сейчас стало лучше.
Давайте честно: с тз читателя вы сделали не решение, а примерно описали в каком направлении копать и ряд подводных камней. Повторить ваше решение даже по статье сможет достаточно небольшой процент разработчиков. Это не претензия, если что. Кстати, я звездочками пометил пару репозиториев на github, за это спасибо. Проблема, на мой взгляд, что очень многие все еще говорят о том, как делать микросервисы, как писать интеграционные или end-to-end тесты и тд, но очень мало рассказов о том, как это не делать. А "не делать" - это не просто не делать. Это - о том, как не делая достигать поставленных целей. А тут и проблемы с доверием, и сбор бизнес метрик, и куча всего.
На тему end-to-end тестов вот неделю назад как раз читал: https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html?m=1 Но это - Гугл, они спокойно на пользователях в проде тестируют. Лично я выпускал успешные проекты без единого автоматизированного end-to-end, т.ч., да, я готов ставить под сомнение их целесообразность. И, если что, в моем понимании, интеграционные тесты - testcontainers, sql проверить, например. А ваше решение для меня намного ближе к end-to-end.
Я изначально свою ветку начал с того, что призвал читателей так не делать. Т.е. все это обсуждение - о целесообразности. Это не значит, что статья бесполезная, если что. Мой посыл, скорее, - почитайте, узнайте что-то новое, но 10 раз подумайте точно ли оно вам надо, перед тем, как копировать.
Конфигурацию и инфраструктуру? Так это не сквозным тестированием проверить можно.
А ежели не секрет, то чем?
Для какого уровня подготовки эта статья?
Немного по тексту статьи.
Для проверки на доступность порта я пользовался примерно таким кодом: https://stackoverflow.com/a/48828373 (см setReuseAddress)
Для проверки на дебаггер:
boolean IS_IN_DEBUG = ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("jdwp");
Возможно, менее универсально, чем у вас, но мне хватало.
Тоже активно использую Nanocloud, но в менее глобальном контексте, конкретно для тестирования распределенных вычеслений для Hazelcast и ApacheIgnite.
Сквозное и интеграционное тестирование просто, как юнит-тесты