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

Обычная история

Все мы знаем о противоречии между непрерывностью поставок и надежностью при работе с критически важными производственными системами. С одной стороны, мы хотим непрерывно поставлять продукцию, а с другой стороны, мы хотим, чтобы производство не росло. Это обычная история: инженерная организация начинает с быстрого продвижения и частых выпусков. Через несколько месяцев, может быть, год или около того, система становится настолько сложной, что понятия «быстро» и «надежно» больше несовместимы. Вещи начинают ломаться. Важные люди начинают жаловаться. Внезапно в процесс разработки внедряются ручные процессы проверки. Развитие становится медленным. Теперь все жалуются.

Звучит знакомо?

Хотя это обычная история, я не чувствую, что она неизбежна. Можно непрерывно поставлять и поддерживать надежную производственную систему. Для уточнения, под «частым выпуском» я имею в виду несколько раз в день (для производства) и не более нескольких минут церемонии. Под «надежным» я подразумеваю что-то вроде 4 или 5 девяток.

Все дело в доверии

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

Важно отметить, что вы не можете заменить доверие к своим тестам тщеславными показателями, такими как покрытие кода. Часто компании с высоким охватом кода по-прежнему открывают браузер и начинают нажимать на что-то, чтобы выполнить «проверку работоспособности» во время развертывания. Это потому, что хотя в терминале много зеленого, доверия просто нет.

Вы не можете заменить доверие к своим тестам тщеславными показателями, такими как покрытие кода.

Во всех моих усилиях по тестированию «доверие» — это то, что мне нужно. Я просто хочу знать, что система работает, и я хочу, чтобы автоматизированный процесс проверил ее для меня. Это открывает возможность для меня и моей команды непрерывно доставлять продукты безопасным способом.

Методы тестирования

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

Если вы можете подтвердить, что производство работает, то производство работает! Если вы можете подтвердить, что постановка работает, то, надеюсь, работает и производство.

Контрактные тесты как конечные точки

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

Давайте рассмотрим этот своеобразный тест.

Во-первых, обратите внимание, что это не тест Mocha или Jest, а конечная точка Express! Функция it — это простой в написании помощник, который я придумал для этого примера, но он похож на то, что я использую в реальной жизни. Сделав тест конечной точкой, вы получите следующие преимущества:

  • Дополнительная настройка среды тестирования не требуется. Запуск службы является настройкой тестовой среды.
  • Этот тест легко запустить в любой среде; просто позвоните в конечную точку.
  • Результат этого теста легко получить и включить в процесс развертывания для проверки после развертывания.
  • Служба может вызывать свои собственные тестовые конечные точки при запуске и самостоятельно проверять свое поведение. Разве не было бы здорово, если бы услуга просто была онлайновой и подтверждала, что каждая ее часть работает?
  • Если у всех служб есть эти конечные точки, эта служба может вызвать тестовые конечные точки служб, которые соприкасаются с ней в графе микрослужбы, чтобы проверить, работают ли они — это огромно!

Во-вторых, важно отметить, что этот тест проверяет API службы, проверяя его возможности чтения и записи. Ничто не высмеивается; все реально. Если этот тест пройден, почти гарантировано, что счастливый путь для [POST] /api/users работает.

В-третьих, что такое testId? testId — это гайд, который можно где-то сохранить, чтобы через некоторое время тестовые данные можно было удалить из базы данных. Все тестовые данные будут иметь столбец testId, содержащий идентификатор теста. Это позволяет тестовым данным находиться в производственной базе данных вместе с нетестовыми данными. Для перемещения testId вместе с запросом через распределенную систему должна быть принята стратегия, аналогичная идентификатору корреляции.

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

При каждом запуске теста будет создаваться новый пользователь, чей адрес электронной почты не будет конфликтовать ни с какими ранее созданными пользователями или фактическими адресами электронной почты пользователей.

«Всегда включенные» тесты

Поскольку этот тест можно запускать несколько раз в любой среде без неблагоприятных побочных эффектов, почему бы не запускать его в рабочей среде каждые несколько минут? Если тесты «всегда включены», ваша производственная система будет постоянно проверяться. Это поможет обнаружить многочисленные виды дефектов, которые могут возникнуть в производственной среде (сетевые разделы, ключи с истекшим сроком действия, поврежденные узлы, неправильное развертывание смежных служб и т. д.).

Чем хороши контрактные тесты в качестве конечных точек

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

Утверждения телеметрии/регистрации

Простой тест чтения/записи, подобный приведенному выше, вероятно, является лучшим сценарием в реальной жизни. Замечательно, когда вы можете проверить, работает ли ваша система, просто запросив состояние ожидаемого изменения. Однако бывают случаи, когда 1) вы используете что-то вроде стороннего API, который не может обрабатывать тестовые данные, или 2) нет состояния для запроса, чтобы проверить, что все работает. Рассмотрим следующий код. Предположим, что платежный шлюз нельзя вызвать с тестовыми данными, иначе он взорвется:

Как вы это проверяете? Есть несколько способов сделать это, но использование утверждений телеметрии — один из моих любимых, поскольку они имеют множество преимуществ. Переосмыслите приведенный выше код. Теперь он понимает, что такое testId, и записывает некоторую телеметрию:

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

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

Здесь есть важный принцип: сделать все доступным для запросов. Не просто снимайте деньги с кредитной карты пользователя; создайте запрашиваемую запись об этом, чтобы ее можно было утверждать. Это относится не к записи в базу данных, которая по своей сути является запрашиваемой, а ко многим типам команд (отправка электронной почты, снятие средств с кредитной карты и т. д.). не оставляют следов того, что они произошли, с точки зрения тестирования. Если у вас есть какой-либо запрашиваемый журнал событий, вы сможете утверждать, что происходит в вашей системе, запрашивая журнал.

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

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

Забавное примечание: может быть возможным, если ваша телеметрия достаточно хороша, что вам нужно только выполнить тесты утверждения телеметрии для проверки правильности вашей системы. поведение. Для такой телеметрии я бы рекомендовал effects-as-data.

Запись телеметрии

Мой предпочтительный способ записи этой телеметрии — поместить ее в тему Кафки. Затем я бы настроил двух потребителей. Первый запишет данные в запрашиваемой форме (например, Mongo с TTL), чтобы их можно было использовать, как указано выше. Второй запустит базовое обнаружение аномалий в данных. Например, я хотел бы знать, прекратится ли мой постоянный поток платежей по кредитным картам!

Часть 1 Заключение

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