Русский
Русский
English
Статистика
Реклама

Интеграционные тесты

Вам не нужны юнит-тесты

18.12.2020 12:07:33 | Автор: admin

Да, вы не ослышались именно так! В IT-сообществе прочно укоренилось мнение, что все эти тесты вам хоть как-то помогают, но так ли это на самом деле? Вы сами пробовали мыслить критически и анализировать это расхожее мнение? Хипстеры придумывают кучу парадигм TDD, BDD, ПДД, ГИБДД лишь чтобы создать иллюзию бурной деятельности и хоть как-то оправдать свою зарплату. Но задумайтесь, что будет, если вы (либо ваши программисты) начнете все свое время уделять исключительно написанию кода? Для тестирования есть отдельное направление и целые подразделения. Вы же не заставляете программистов писать требования, так? Тогда почему они должны писать тесты? Всех согласных и несогласных прошу проследовать внутрь поста, где я вам наглядно покажу, что юнит (и интеграционные) тесты великое зло!

Откуда вообще пошло тестирование

В стародавние времена никакого тестирования не было в принципе. Не было даже такого направления, что уж и говорить про такие термины, как блочное (модульное) и интеграционное тестирование. А про всякие e2e и, прости господи, пайплайны, я вообще молчу. И все это потому, что тестировать, собственно, было еще нечего. В те годы инженеры-программисты только пытались создать первые ЭВМ.

Как нам всем известно, первые ЭВМ были гигантских размеров, весили десятки тонн и стоили дороже этих ваших Apple MacBook Pro Retina 4k 512mb RAM 1Tb SSD Touch Bar USB Type-C. И в те времена разработчики действительно боялись, что во время работы что-нибудь пойдет не так. Думаю, вам известна история возникновения термина баг (bug) если вдруг нет, то почитайте, это очень интересно. И, так как программисты боялись всего на свете, они и придумали модульное тестирование.

Времена менялись, менялись и ЭВМ. Тестирование тоже менялось. Помимо блочных тестов, возникло также и целое направление, которое впоследствии получило название Quality Assurance.

Но разработчики тоже менялись. В наше время становится смешно от мысли, что кто-то боится запустить программу, потому что от этого может загореться сервер. В 2020 году программисты не боятся запускать свои программы. А если нет страха зачем тестировать?

Современные реалии

Повторю свой вопрос если ваш MacBook (или Xiaomi) не взорвется из-за ошибки в коде, зачем тогда тестировать? Вы просто запускаете и наслаждаетесь результатом. Предвосхищая ваше негодование по поводу дороговизны ошибок для заказчика пускай тестированием занимаются специально обученные люди.

На последнем хочу слегка заострить внимание. В современной разработке основная стоимость кроется не в аппаратном, а в программном обеспечении. И ошибки по-прежнему стоят дорого. Но ответственность за эти ошибки плавно перекочевала с плеч разработчиков на плечи тестировщиков. Как-никак, это они назвали себя Quality Assurance а раз проводишь проверку качества, делай это качественно \_()_/

В конце концов, отдел разработки называется Software Development, а не Unmistakable Development. Мы никому ничего не обещаем.

Хороший программист уверен в себе

Когда вы покрываете свой код юнит-тестами, вы будто заявляете всему миру: Смотрите, я не уверен в том, что оно работает. Будут ли вас за такие мысли уважать более опытные коллеги и начальник? Будет ли вам доверять заказчик?

Просто откройте свой проект и задумайтесь. Вы умный и образованный человек. Вы хороши собой. Зачем вам балласт в виде модульных тестов, которые, ко всему прочему, еще и портят вашу репутацию?

Задание: Прямо сейчас скажите себе Я уверен в качестве своего кода и удалите все юнит-тесты из проекта.

В идеальном случае, вы должны удалить все тесты прямо из ветки master, однако, это может быть запрещено правилами репозитория. В таком случае вам придется делать pull request и убеждать своих старомодных коллег в том, что тесты пережиток прошлого. Однако, как только ваши соратники примкнут к вашему мировоззрению, на проекте воцарится гармония.

Запомните несколько простых постулатов:

  1. Хороший программист не пишет тесты, так как не сомневается в качестве своей работы.

  2. Превосходный программист даже не компилирует код перед тем, как запушить изменения на прод. Нет, дело не в том, что он настолько крут, что компилирует все в уме, вовсе нет. Просто он, опять-таки, не сомневается в своих знаниях и качестве своего кода.

  3. Тщетные попытки найти ошибки в вашем коде оставьте тестировщикам.

Тесты отнимают время

Время программистов дорогое. Время тестировщиков дешевое. Какой тогда смысл заставлять программистов писать тесты? Это невыгодно даже с финансовой точки зрения.

Ученые давно выяснили, что постоянное переключение между контекстами истощает ресурсы мозга. Если вы погонитесь за двумя зайцами одновременно, ни одного не поймаете. Вы не будете продуктивны ни в написании кода, ни в написании тестов. А если же вы будете все свое время посвящать исключительно программированию, ваша продуктивность устремится вверх по экспоненте.

В конце концов, тестировщики тоже люди, и не стоит лишать их хлеба. Если вы будете сами тестировать свой код, весь отдел QA станет попросту не нужен, и бедолаг уволят. Вспомните, что было во времена промышленного переворота в США когда машины начали заменять людей на производстве, начались самые настоящие бунты.

Поэтому не будьте машиной. Не провоцируйте тестировщиков на поднятие бунта.

Парадигмы запутывают

Unit-testing, Integration Testing, End 2 End, Pipelines, CI, CD что вы еще придумаете, лишь бы не работать? Есть мнение, что когда программист выгорает и начинает прокрастинировать, он идет настраивать пайплайн.

Все эти парадигмы и термины запутывают. Опять же, ваши мозговые ресурсы мгновенно тратятся, когда вы начинаете думать про e2e вместо того, чтобы просто программировать. Все это пугающе отвлекает вас и не дает сосредоточиться на главном на написании кода.

Если кому-то надо настроить CI или CD пускай настраивают сами. Пусть это сделает devops, в конце концов. Если вас будут просить как-либо помочь в настройке, смело отказывайтесь и ссылайтесь на свою занятость наиважнейшими и перво-приоритетными задачами, а именно написанием кода.

Вам не нужно знать ничего лишнего. Иными словами, вы программируете. Тестировщики тестируют. Девопсы ковыряются во всяких скриптах на bash. Менеджеры ну, менеджеры есть менеджеры.

Delivery In Time

Я предлагаю ввести лишь одно простое понятие: DIT Delivery In Time. Это схоже с известной парадигмой ППКБ (Просто Пиши Код Б****), но звучит гораздо современнее и толерантнее. Парадигма ППКБ ставит программистов в центр мироздания и не считается с работой других членов команды. Это, как минимум, неуважительно. В DIT мы верим, что программисты скромные служители, единственной целью которых является написание кода. При всем этом, мы не закрываем глаза на работу других коллег и уважаем их труды. Просто мы считаем, что каждый должен быть занят своим делом: программисты программировать, тестировщики тестировать, и тд. Когда каждый будет делать то, чему обучен, сроки перестанут срываться.

Парадигма DIT предлагает сплошные бонусы заказчикам. Они могут нанять исключительно разработчиков, чтобы те ППКБ (просто писали код), и все их бюджеты будут направлены непосредственно на создание продукта. При желании заказчик может также нанять и тестировщиков. То есть, простите, Quality Assurance инженеров. А может и не нанимать и запустить тестирование в продакшене.

Я однажды слышал один забавный диалог:

Сколько человек сейчас тестирует нашу систему?
Один человек.
Мы только что выкатили ее на прод.
Ну значит, нашу систему тестирует 1000 человек.

И это правильно. Можете платить штатным тестировщикам, а можете нанять тысячи внештатных совершенно бесплатно.

При этом стоит держать в голове, что парадигма DIT гарантирует, что программный код будет написан вовремя, потому что разработчики будут заняты только своей работой. Но если подключать тестировщиков, то сроки сдвинутся как минимум на время их работы. А если они найдут какие-то баги (что, разумеется, маловероятно, ведь хорошие программисты уверены в своей работе), то сроки сдвинутся еще немного.

Совет: Чтобы не срывать сроки и доставлять вовремя лучше нанять разработчиков, а тестированием заниматься на продакшене. Даже если что-то пойдет не так, вы всегда можете возразить, что соблюли сроки, как и было обговорено. А о большем и не договаривались.

Про интеграционное тестирование

С модульным тестированием вроде разобрались, настало время поговорить о тестировании интеграционном. Именно оно отнимает больше всего времени.

Когда-то я был молодым и верил в то, что тесты (юнит, интеграционные, да всякие) несут добро. Хорошо написанные тесты гарантировали отсутствие регрессии, то есть вы могли изменять и рефакторить код без боязни, что вы где-то ошиблись. Выглядит здорово, правда? Делаешь кучу правок, запускаешь тесты и смотришь, допустил ли ты ошибку.

Но теперь я повзрослел. Я зрю в корень проблемы, а не на ее последствия. И корнем проблемы является человек по ту сторону монитора, в то время как ошибки в тестах лишь ее последствия. Если улучшить, прокачать навыки программиста, то проблема решится естественным образом, и любые дополнительные проверки утратят актуальность.

Всю эту привычку гонять тесты я могу сравнить с неуверенным в себе поваром в ресторане. Представьте на минутку, что вы шеф, и приготовили фирменное блюдо ресторана. Вы уверены в том, что оно вкусное? Может, вам стоит прогнать тесты и, скажем, бросить кусок блюда ресторанной собаке? Право же, смешно. Разумеется, вы просто берете и гордо выносите блюдо клиентам без каких-либо опасений. Вы профессионал и уверены в своей работе.

Но я отошел от темы. Интеграционные тесты (кстати, е2е тоже) это большие усилия. Вы поднимаете несколько компонентов с полными контекстами, или даже несколько систем, и начинаете пересылку сообщений из одной в другую. Вы продумываете тестовые сценарии, причем меньше положительных и больше отрицательных, чтобы удостовериться в работоспособности кода. Вы начинаете думать как тестировщик и вести себя как тестировщик. Вы на грани.

Понимаете, к чему я клоню? Вы делаете чужую работу и отнимаете чужую зарплату. Вы становитесь машиной. А я уже напоминал, к чему может привести промышленный переворот.

Просто будьте собой

Будьте собой и будьте счастливы. Определите свои ценности и следуйте выбранному курсу. Цель программиста созидать, в то время как цель тестировщика разрушать. Вы не можете создавать что-то новое лишь для того, чтобы это затем разрушить. Ведь тогда вы вступите в конфликт с самим собой, со своей сущностью. Именно поэтому вам не следует писать тесты на свой собственный код.

Просто будьте собой!

В качестве заключения

Если вы дочитали до этого момента и не бросились писать гневный комментарий, то либо вы прекрасно понимаете важность тестов и сразу заметили иронию, либо просто обратили внимание на теги :)

Друзья, это были вредные советы. Однако, навеяны они были реальными историями. Сейчас у меня на рабочих проектах хорошее покрытие, и это действительно сильно облегчает работу. Но однажды, давно, я попал на проект с покрытием бранчей в районе 15%. Мы не вылезали из регрессии. Примерно тогда я начал осознавать всю важность тестов и стал задумываться о том, почему некоторые из нас ими пренебрегают.

А какой процент покрытия в ваших проектах? Дотягивает ли покрытие линий/веток до 80%? Или болтается где-то в районе 30? Если у вас частая регрессия и низкое покрытие вы догадываетесь, что стоит изменить?

Я понимаю, что подобный пост не совсем по тематике Хабра. Но сегодня пятница, к тому же на носу Новый Год, так что давайте немного расслабимся :)

И, пользуясь случаем: если вам понравился такой формат, то приглашаю вас в свой паблик вк, где я и выкладываю время от времени подобные саркастические посты, которые зачастую неудобно выкладывать сюда.

Подробнее..

Перевод Вы неправильно используете docker-compose

09.04.2021 08:10:47 | Автор: admin

Вот краткое изложение некоторых кардинальных "грехов", которые я совершил при использовании docker-compose.

Вы познакомились с docker-compose либо по собственному выбору, либо по необходимости. Вы используете его некоторое время, но считаете его неуклюжим. Я здесь, чтобы сказать вам: "Вы, вероятно, неправильно его используете".

Хорошо, возможно я преувеличиваю. Я не думаю, что на самом деле существует стопроцентно правильный или неправильный способ его использования: локальные сборки и настройки для разработчиков, как правило, имеют разные порой необычные требования, и поэтому стандарт не всегда соответствует реальности. Пожалуйста, отнеситесь к статье с соответствующим скептицизмом.

В любом случае, вот краткое изложение некоторых из кардинальных "грехов", которые я совершил при использовании docker-compose.

В этой статье я сосредоточусь на сценариях использования, связанных с интеграционным тестированием и использованием docker-compose в качестве среды разработки. Я думаю, что для производственного использования docker-compose обычно не подходит.

Грех 1: Вы используете сеть хоста

Одна из первых вещей, которые новички находят обременительными - это использование сетей Docker. Это еще один уровень знаний, который нужно добавить в свой багаж после того, как вы привыкнете к основам docker build и docker run ... и, честно говоря, зачем вам вообще разбираться в этих сетях Docker? Все нормально работает через сеть хоста, правда? Неправильно!

Использование сети хоста означает, что вам необходимо зарезервировать определенные порты для различных микросервисов, которые вы используете. Если вам доведется поднять два стека, которые имеют одинаковые порты - не повезло. Если вы хотите создать две версии одного и того же стека - не повезло. Вы хотите протестировать поведение определенного сервиса, когда у него несколько реплик?

По умолчанию docker-compose запускает свои контейнеры в отдельной сети с именем имякаталога_default. Так что на самом деле вам не нужно делать ничего особенного, чтобы воспользоваться преимуществами сетей Docker.

Эта сеть сразу дает вам ряд преимуществ:

  • Эта сеть более изолирована от вашей сети хоста, поэтому маловероятно, что особенности вашего системного окружения приведут к изменению поведения настройки сборки. У вас есть доступ к Интернету, но любые порты, которые вы хотите сделать доступными с хоста, должны быть объявлены с привязкой порта.

  • Если служба начинает прослушивать 0.0.0.0 (как должны делать контейнеры), то настройка сети хоста откроет этот порт в вашей локальной сети. Если вы используете сеть Docker, она предоставит доступ только к этому порту.

  • Сервисы смогут общаться, используя их имена в качестве имен хостов. Итак, если у вас есть служба с именем db и в ней есть служба, прослушивающая порт 5432, вы можете получить к ней доступ из любой другой службы через db: 5432. Обычно это более понятно, чем localhost: 5432. А поскольку нет риска конфликта портов localhost, у нас больше шансов избежать ошибок при использовании портов в разных проектах.

  • Большинство портов не нужно открывать для хоста - это означает, что они не конкурируют за глобальные ресурсы, если вам нужно увеличить количество контейнеров с помощью --scale.

Грех 2: Вы привязываете порты к 0.0.0.0 хоста

Я видел это везде, вы видели это везде, все видели это везде: привязка портов как 8080:8080. На первый взгляд это выглядит безобидно. Но дьявол кроется в деталях. Эта чрезвычайно распространенная привязка портов не просто перенаправляет порт контейнера на локальный хост - она перенаправляет его, чтобы он был доступен на каждом сетевом интерфейсе в вашей системе, включая все, что вы используете для подключения к Интернету.

Это означает, что очень вероятно, что ваши контейнеры разработки постоянно прослушивают вашу локальную сеть - когда вы дома, когда вы в офисе или когда вы находитесь в McDonalds. Это всегда доступно. Это может быть опасно. Не делай этого.

Но я использую ufw, мои порты по умолчанию недоступны.

Это может быть правдой, но если вы используете эту настройку docker-compose в команде, у одного из ваших товарищей по команде может не быть брандмауэра на своем ноутбуке.

Исправить очень просто: просто добавьте 127.0.0.1: впереди. Так, например, 127.0.0.1:8080:8080. Это просто указывает докеру, чтобы он открывал порт только для петлевого сетевого интерфейса и ничего больше.

Грех 3: Вы используете helthy для координации запуска служб

Я хотел бы в кое-чем признаться. Я на 100% виноват в этом.

Основная причина, по которой эта проблема такая важная, заключается в том, что Docker или Docker Compose не поддерживают ее. Версия 2.1 формата docker-compose имела параметр depends_on для которого можно было установить значение service_healthy. Кроме того, каждая служба может иметь команду проверки работоспособности, которая может сообщать docker-compose - healthy. Что ж этого больше нет в версии 3.0 и никакой замены для него не предлагается.

Документация Docker в основном рекомендует сделать ваш сервис устойчивым к отсутствию других сервисов, потому что это то, что в любом случае может произойти в производственной среде, например если будет прерывание сигнала сети или если сервис перезапустится. Не могу поспорить с этой логикой.

Это становится немного более обременительным когда вы запускаете интеграционный тест и процедуры, предназначенные для инициализации тестовой среды (например, предварительное заполнение базы данных некоторыми тестовыми данными) службы в конечном итоге могут не запуститься до того, как другая служба будет готова. Таким образом, аргумент о том, что в любом случае он должен быть устойчивым в производственной среде, здесь не совсем применим, потому что код для заполнения БД тестовыми данными никогда не используется в производственной среде.

В таких случаях вам нужно что-то, что ждет готовности сервисов. Docker рекомендует использовать wait-for-it, Dockerize или wait-for. Однако обратите внимание, что готовность порта не всегда является признаком того, что служба готова к использованию. Например, в интеграционном тесте с использованием определенной базы данных SQL с определенной схемой порт становится доступным при инициализации базы данных, однако тест может работать только после применения определенной миграции схемы. Сверху могут потребоваться проверки для конкретного приложения.

Проблема 4: Вы запускаете БД в режиме docker-compose, а тест - на хосте

Вот ситуация: вы хотите запустить несколько модульных тестов, но эти тесты зависят от некоторых внешних служб. Может быть база данных, может быть Redis, может быть другой API. Легко: давайте поместим эти зависимости в docker-compose и подключим к ним unit test.

Это здорово, но учтите, что ваши тесты больше не являются просто модульными. Теперь это интеграционные тесты. Следует принять во внимание еще одно важное различие: вам нужно будет учесть настройку тестовой среды и teardown. Обычно лучше всего выполнять setup/teardown вне тестового кода - основная причина в том, что может быть несколько разных пакетов в зависимости от этих внешних служб

Если вы в конечном итоге разделите настройку теста и teardown, вы можете, приложив дополнительные усилия, поместить свой интеграционный тест в контейнер.

Контейнерные тесты означают:

  • Вы находитесь в той же сети Docker, поэтому настройка подключения такая же, как и для запуска службы в Compose. Конфигурация становится чище.

  • Интеграционный тест не зависит от какой-либо другой конфигурации локальной системы или настройки среды, например ваших учетных данных JFrog или каких-либо зависимостей сборки. Контейнер изолирован.

  • Если другой группе необходимо запустить ваши тесты для обновленной версии службы, от которой зависят тесты, вы можете просто поделиться образом интеграционного тестирования - им не нужно компилировать или настраивать набор инструментов для сборки.

  • Если в результате получается несколько отдельных контейнеров для интеграционных тестов, как правило, их можно запускать все одновременно и параллельно.

Совет по использованию контейнерных интеграционных тестов - использовать для них отдельное определение docker-compose. Например, если большинство ваших сервисов существует в docker-compose.yml, вы можете добавить docker-compose.test.yml с настройками интеграционных тестов. Это означает, что docker-compose up вызывает ваши обычные службы, а docker-compose -f docker-compose.yml -f docker-compose.test.yml up запускает ваши интеграционные тесты. Полный пример того, как этого добиться, можно найти в этом отличном репозитории для интеграционного тестирования docker-compose от Ardan Labs.

Хорошо, хорошо - называть это неправильным не совсем справедливо. Есть много ситуаций, когда предпочтительнее не использовать контейнеры. В качестве простого примера, многие языки имеют глубокую интеграцию IDE, что делает вставку контейнера между языком и IDE практически невозможным. Есть много веских причин не делать этого.

Заключение

Docker Compose может быть отличным инструментом для локальной разработки. Хотя у него есть несколько подводных камней, он обычно приносит много преимуществ командам разработчиков, особенно когда используется в сочетании с интеграционными тестами.

Анонсы других публикаций и возможность комментирования (если тут не можете) втелеге.

Подробнее..

Для чего нужно интеграционное тестирование?

06.05.2021 08:19:11 | Автор: admin

Эта статья является конспектом книги Принципы юнит-тестирования. Материал статьи посвящен интеграционным тестам.

Юнит-тесты прекрасно справляются с проверкой бизнес-логики, но проверять эту логику в вакууме недостаточно. Необходимо проверять,как разные ее части интегрируются друг с другом и внешними системами: базойданных, шиной сообщений и т. д.

В этой статье рассматривается роль интеграционных тестов, когда их следует использовать и когда лучше положиться на классические юнит-тесты. Также затронем эффективное написание интеграционных тестов.

Что такое интеграционный тест?

Юнит-тест удовлетворяет следующим трем требованиям:

  • проверяет правильность работы одной единицы поведения;

  • делает это быстро;

  • и в изоляции от других тестов.

Тест, который не удовлетворяет хотя бы одному из этих трех требований, относитсяк категории интеграционных тестов.На практике интеграционные тесты почти всегда проверяют, как система работает в интеграции с внепроцессными зависимостями.

Если все внепроцессные зависимости заменить моками,никакие зависимости не будут совместно использоваться между тестами, благодарячему эти тесты останутся быстрыми и сохранят свою изоляцию друг от друга и становятся юнит-тестами. Темне менее во многих приложениях существует внепроцессная зависимость, которуюневозможно заменить моком. Обычно это база данных зависимость, не видимаядругими приложениями.

Важно поддерживать баланс между юнит- и интеграционными тестами. Работанапрямую с внепроцессными зависимостями замедляет интеграционные тесты.Кроме того, их сопровождение также обходится дороже.

С другой стороны, интеграционные тесты проходят через больший объем кода, что делает их болееэффективными в защите от багов по сравнению с юнит-тестами.Они также более отделены от рабочего кода, а, следовательно, обладают большейустойчивостью к его рефакторингу.

Соотношение между юнит- и интеграционными тестами зависит от особенностейпроекта, но общее правило выглядит так: проверьте как можно больше пограничныхслучаев бизнес-сценария юнит-тестами; используйте интеграционные тесты дляпокрытия одного позитивного пути, а также всех граничных случаев, которые непокрываются юнит-тестами.

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

Какие из внепроцессных зависимостей должны проверяться напрямую

Все внепроцессные зависимости делятся на две категории.

  • Управляемые зависимости (внепроцессные зависимости, находящиеся под вашимполным контролем): эти зависимости доступны только через ваше приложение;взаимодействия с ними не видны внешнему миру. Типичный пример база данных.

  • Неуправляемые зависимости (внепроцессные зависимости, которые не находятсяпод вашим полным контролем) результат взаимодействия с такими зависимостямивиден извне. В качестве примеров можно привести сервер SMTP и шину сообщений.

Взаимодействия с управляемыми зависимостями относятся к деталям имплементации. И наоборот, взаимодействия с неуправляемымизависимостями являются частью наблюдаемого поведения вашей системы.

Рис. 1 Взаимодействия с зависимостямиРис. 1 Взаимодействия с зависимостями

Это различие приводит к тому, что такие зависимости по-разному обрабатываютсяв интеграционных тестах. Взаимодействия с управляемыми зависимостями являются деталями имплементации. Использоватьих следует в исходном виде в интеграционных тестах. Взаимодействия с неуправляемыми зависимостями являютсячастью наблюдаемого поведения системы. Такие зависимости должны заменяться моками.

Требование о сохранении схемы взаимодействий с неуправляемыми зависимостями обусловлено необходимостью поддержания обратнойсовместимости с такими зависимостями. Моки идеально подходят для этой задачи.Они позволяют обеспечить неизменность схемы взаимодействий при любыхвозможных рефакторингов.Поддерживать обратную совместимость во взаимодействиях с управляемыми зависимостями не обязательно, потому что никто, кроме вашего приложения, с ними неработает. Внешних клиентов не интересует, как устроена ваша база данных; важнотолько итоговое состояние вашей системы. Использование реальных экземпляровуправляемых зависимостей в интеграционных тестах помогает проверить это итоговое состояние с точки зрения внешних клиентов.

Иногда встречаются внепроцессные зависимости, обладающие свойствами какуправляемых, так и неуправляемых зависимостей. Хорошим примером служит базаданных, доступная для других приложений.

База данных не лучший механизм для интеграции между системами, потому чтоона связывает эти системы друг с другом и усложняет дальнейшую их разработку.Используйте это решение только в случае, если других вариантов нет. Правильнееосуществлять интеграцию через API (для синхронных взаимодействий) или шинусообщений (для асинхронных взаимодействий).

В этом случае следует рассматривать таблицы, видимые для других приложений, как неуправляемую зависимость. Такие таблицы фактически выполняют функции шины сообщений, а их строки играют роль сообщений. Используйтемоки, чтобы гарантировать неизменность схемы взаимодействий с этими таблицами. В то же время следует рассматривать остальные части базы данных как управляемую зависимость и проверять ее итоговое состояние, а не взаимодействия с ней.

Рис. 2 БД, доступная для внешних приложенийРис. 2 БД, доступная для внешних приложений

Основные приемы интеграционного тестирования

Существует несколько общих рекомендаций, которые помогут извлечь максимальную пользу из интеграционных тестов:

  • явное определение границ доменной модели (модели предметной области);

  • сокращение количества слоев в приложении;

  • устранение циклических зависимостей.

Явное определение границ модели предметной области. Доменная модель представляет собой совокупность знанийо предметной области задачи, для решения которой предназначен ваш проект. Данная практика помогает с тестированием. Юнит-тесты должны ориентироваться на доменную модель и алгоритмы, тогдакак интеграционные тесты на контроллеры. Таким образом, четкое разграничениемежду доменными классами и контроллерами также помогает отделить юнит-тестыот интеграционных.

Сокращение количества слоев. Многие разработчики стремятся к абстрагированию и обобщению кода путем введения дополнительных уровней абстракции.

Рис. 3 Типичное корпоративное приложение с несколькими слоямиРис. 3 Типичное корпоративное приложение с несколькими слоями

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

Все проблемы в программировании можно решить путем добавления нового уровня абстракции (кроме проблемы наличия слишком большого количества уровней абстракции).
Дэвид Дж. Уилер

Лишние абстракции также затрудняют юнит- и интеграционное тестирование.Кодовые базы со слишком большим количеством слоев обычно не имеют четкойграницы между контроллерами и моделью предметной области. Старайтесь ограничиться минимально возможным количеством уровней абстракции.В большинстве серверных систем можно обойтись всего тремя: слоем доменноймодели, слоем сервисов приложения (контроллеров) и слоем инфраструктуры.

Исключение циклических зависимостей. Циклическая зависимость возникает в том случае, если два или более класса прямо иликосвенно зависят друг от друга. Типичный пример циклической зависимости обратный вызов:

public class CheckOutService{  public void CheckOut(int orderId)  {    var service = new ReportGenerationService();    service.GenerateReport(orderId, this);    /* остальной код */  }}public class ReportGenerationService{  public void GenerateReport(    int orderId,    CheckOutService checkOutService)  {  /* вызывает checkOutService при завершении генерирования */  }}

Как и в случае с избыточными уровнями абстракции, циклические ссылки создаютдополнительную когнитивную нагрузку при попытке прочитать и понять код. Циклические зависимости также усложняют тестирование. Вам часто приходитсяиспользовать интерфейсы и моки, для того чтобы разбить граф классов и изолировать одну единицу поведения.

Что же делать с циклическими зависимостями? Лучше всего совсем избавитьсяот них. Отрефакторить класс ReportGenerationService, чтобы он не зависел от CheckOutService и сделать так, чтобыReportGenerationService возвращал результат работы в виде простого значениявместо вызова CheckOutService:

public class CheckOutService{  public void CheckOut(int orderId)  {    var service = new ReportGenerationService();    Report report = service.GenerateReport(orderId);    /* прочая работа */  }}public class ReportGenerationService{  public Report GenerateReport(int orderId)  {  /* ... */  }}

Использование нескольких секций действий в тестах

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

  • подготовка подготовка данных для регистрации пользователя;

  • действие вызов UserController.RegisterUser();

  • проверка запрос к базе данных для проверки успешного завершения регистрации;

  • действие вызов UserController.DeleteUser();

  • проверка запрос к базе данных для проверки успешного удаления.

Лучше всего разбить тест, выделив каждое действие в отдельный тест. На первыйвзгляд это может показаться лишней работой, но эта работа окупается в долгосрочной перспективе. Фокусировка каждого теста на одной единице поведения упрощает пониманиеи изменение этих тестов при необходимости.

Исключение из этой рекомендации составляют тесты, работающие с внепроцесснымизависимостями, трудно приводимыми в нужное состояние. Например, регистрация пользователя приводит к созданию банковского счета во внешнейбанковской системе. Банк предоставил вашей организации тестовую среду, которую вы хотите использовать для сквозных тестов. К сожалению, тестовая средаработает слишком медленно; также возможно, что банк ограничивает количествообращений к этой тестовой среде. В таком сценарии удобнее объединить несколькодействий в один тест, чтобы сократить количество взаимодействий с проблемнойвнепроцессной зависимостью.

Выводы

Интеграционные тесты проверяют, как ваша система работает в интеграциис внепроцессными зависимостями.

Интеграционные тесты покрывают контроллеры; юнит-тесты покрывают алгоритмы и доменную модель.

Интеграционные тесты обеспечивают лучшую защиту от багов и устойчивостьк рефакторингу; юнит-тесты более просты в поддержке и дают более быструюобратную связь.

Порог для написания интеграционных тестов выше, чем для юнит-тестов: ихэффективность по метрике защиты от багов и устойчивости к рефакторингу должна быть выше, чем у юнит-тестов, для того чтобы скомпенсировать дополнительную сложность в поддержке и медленную обратную связь. Пирамидатестирования отражает этот компромисс: большинство тестов должны составлять быстрые и простые в поддержке юнит-тесты при меньшем количествемедленных и более сложных в поддержке интеграционных тестов, проверяющихправильность системы в целом.

Управляемые зависимости представляют собой внепроцессные зависимости, доступ к которым осуществляется только через ваше приложение. Взаимодействияс управляемыми зависимостями не видимы извне. Типичный пример базаданных приложения.

Неуправляемые зависимости внепроцессные зависимости, доступные длядругих приложений. Взаимодействия с неуправляемыми зависимостями видныснаружи. Типичные примеры сервер SMTP и шина сообщений.

Взаимодействия с управляемыми зависимостями являются деталями имплементации; взаимодействия с неуправляемыми зависимостями являются частьюнаблюдаемого поведения вашей системы.

Иногда внепроцессная зависимость обладает свойствами как управляемых, таки неуправляемых зависимостей. Типичный пример база данных, доступная длядругих приложений. Наблюдаемую часть такой базы следует интерпретироватькак неуправляемую зависимость; заменяйте ее моками в тестах. Рассматривайтеостальную часть зависимости как управляемую проверяйте ее итоговое состояние, а не взаимодействия с ней.

Выделите явное место для модели предметной области в коде. Четкая границамежду классами предметной области и контроллерами помогает отличать юниттесты от интеграционных.

Лишние уровни абстракции отрицательно влияют на вашу способность пониматькод. Постарайтесь свести количество этих уровней к минимуму. В большинствебэкенд-систем достаточно всего трех слоев: предметной области, сервисов приложения и инфраструктуры.

Циклические зависимости увеличивают когнитивную нагрузку при попыткахразобраться в коде. Типичный пример обратный вызов (когда вызываемаясторона уведомляет вызывающую о результате своей работы).

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

Ссылки на все части

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru