Привет, меня зовут Иван, и я зануда.
Сразу скажу, что в моем понимании зануда в тестировании — не тот человек, который всех достал и которого все хотят удушить, а тот, который умеет показать людям, что нужно делать хорошо и не делать плохо, и добиться от них этого. Я считаю, что QA должно расшифровываться как Quality Assistant. Это даже не про Assurance, когда вы обеспечиваете качество, это именно про то, что вы как тестировщик и участник команды помогаете на каждом этапе от требований до выкатки в прод и работы с сопровождением и вашими коллегами добиться того, чтобы каждый этап проходил все лучше и лучше.
В тестировании я уже семь лет, для кого-то это маленький срок, для кого-то — большой, я очень впечатлен коллегами, которые работают уже по 15-20 лет, но развиваюсь, стараюсь нести добро в массы. Одна из моих основных специализаций заключается в том, что я прихожу на проекты, которые начинаются с большой бизнес-идеи, движущейся через много команд. Мне нравятся все вопросы межкомандного тестирования, интеграционного взаимодействия, выстраивания стендов, как драйвить коллег, чтобы мы двигались в одном направлении и не словили на проде кучу ошибок — этим я и занимаюсь. В связи с этим я часто замечаю, что многие команды и коллеги приходят на интеграционные стенды, мы выкатываемся на те стенды, где начинают работать настоящие сервисы на тестовых средах. Я у коллег вижу такие банальные ошибки, которые нельзя было бы пропустить, если бы мы тестировались изолированно на каком-то отдельном кусочке, проверяя свои интеграции еще до поездки на тестовый стенд. Естественно, чем позже мы находим ошибку, тем больше стоимость ее исправления, поэтому нам нужны моки, чтобы мы все это проверяли.
Как мы используем Wiremock
Один из инструментов, который я использую в последнее время, это WireMock. За последний год он очень разросся, давайте про это поговорим. Я вам покажу, с чего начать, мы пройдемся по базовым вещам. Там будет все опи
Все, что нам нужно, это развернуть WireMock.
java -jar wiremock-standalone-3.0.4.jar --local-response-templating --port 8082
Здесь приведена простая команда по запуску его локально, но по факту ваш DevOps легко развернет это в Kubernetes, потому что у WireMock уже есть инструкция — скачивается Docker-образ, запускается, и все, что вам нужно, это рабочий instance для начала.
Как легко убедиться, что у вас запустился WireMock? Мы делаем запрос на localhost. Получаем, что запрос не может быть обработан, но это именно и показывает, что WireMock запустился.
Теперь нам нужно немного Java-кода и магии.
Как создавать проект, надеюсь, многие знают.
Открываем IDE, нажимаем “Создать проект”, выбираем Java.
Нам нужна только одна зависимость.
Я начинал писать это, когда еще была вторая версия WireMock, несколько месяцев назад появилась 3.0.4, сейчас у него, кажется, 3.3.4, они резко стартанули, растут и вообще молодцы. Для того, чтобы начать работать с WireMock, нам нужен объект, который будет управлять этим поднятым instance и настраивать моки.
В примере вы можете видеть, что я всего лишь указываю адрес и порт, берем новый объект WireMock c нужным адресом и портом.
public class Main {
2 usages
private static final String wireMockUrl = "localhost";
2 usages
private static final int portwithWireMock = 8082;
new *
public static void main(String(] args) f
System.out.println(wireMockUrl + " " + portWithWireMock);
WireMock wireMock = new WireMock(wireMockUrl, portwithWireMock);
System.out-println(wireMock.findAllUnmatchedRequests) .get(0).getAbsoluteUrlO);
Далее прописана команда, которая показывает первый несопоставленный запрос и выводит url, по которому он был сделан. Я использую админские ip по управлению. Самые начальные методы работы с этим объектом — можем удалить все моки, очистить историю всех входящих запросов и сбросить состояния всех сценариев.
Вот мы подняли instance, и нам надо с чего-то начать. Так как я делал тестовый запрос на сам url, я для примера решил его и выделить. Как видите, я немного навертел магии и вынес это в отдельный метод.
no usages new*
public static void main(String[] args) {
WireMock wireMock = new WireMock(wireMockUrl, portWithWireMock);
setStubSimpleForGetSomeurl(wireMock);
}
1 usage new*
private static void setStubSimpleForGetSomeurl (WireMock wireMock) {
String urlTostub = "/someurl";
wireMock.register(get(urlPathEqualTo(urlToStub))
-willReturn(ok()));
Дальше буду показывать скриншоты методов и примеров кода, просто знайте, что мы все так же управляем через localhost на определенном порту. Сейчас мы регистрируем Get’овый мок, который будет срабатывать на определенный путь и будет возвращать “OK”. Теперь при обращении на сам url мы вместо сомнительного ответа будем получать 200 OK без тела.
С помощью WireMock можно срабатывать на любой входящий запрос, главное, чтобы было совпадение url и так далее.
Теперь давайте накрутим еще сложнее.
Здесь вы можете видеть, что уже немного изменился синтаксис, и вместо возвращения просто “OK”, мы будем возвращать ответ со статусом 200, с фиксированной задержкой, с определенным телом сообщения и с определенным заголовком, его я прописываю, чтобы в том же Postman красиво шла конвертация на ходу. Вроде стало сложнее, но все понятно, синтаксис достаточно простой. Теперь на наш запрос мы не только возвращаем 200 OK, но вот и наш JSON.
Теперь давайте сделаем так, что у нас мок будет срабатывать не только по определенному пути, но и на наличие какого-то определенного параметра.
Здесь вы видите, что это ожидается в запросе, какой-то параметр someparam, который будет срабатывать по regex на любое входящее наличие каких-то символов. Нам сейчас это не сильно важно, просто мы привязываемся к конкретному параметру в самом запросе. Также мы можем привязаться к конкретным заголовкам, на наличие Cookie и еще разные вещи. Наш мок срабатывает на определенный параметр в запросе, вот мы дописали someparam=13, теперь у нас поменялся ответ на мок.
Сценарии
Еще более сложная вещь. У WireMock есть крутая вещь под названием сценарии.
По факту, вы добавляете в регистрацию моков параметры, где он будет срабатывать, когда он будет срабатывать, на какой статус будет изменена работа при срабатывании сценария.
Теперь вы можете видеть, что я регистрирую два мока по одному url, но с двумя разными ответами. Оба они находятся в одном сценарии, только первый будет срабатывать на статус Started, это дефолтный статус, с которого начинается любой сценарий. При срабатывании он будет переключать статус сценария в статус Fail. Вторая регистрация на тот же путь с теми же настройками говорит, что оно будет срабатывать на статус Fail и при срабатывании будет переключаться уже на Started.
Для чего это вам?
Теперь у нас слева видно, что первый вызов возвращает вам 400 Bad request, потому что это как раз это прописано в статусе ответа, но второй запрос с такими же параметрами уже возвращает 200 OK.
Так у нас работают сценарии, вы это можете использовать в своих настройках. Например, вам нужно проверить ваш сервис, что у него есть перезапросы в случае ошибок. Я как раз использовал для этого. Я настраивал сценарии, сервис приходил, делал первый запрос, получал ошибку. Я видел в логах, что срабатывает повторных запрос самого сервиса, он уже попадает на второй вызов, который возвращает OK, и дальше идет обработка по сервису, то есть я вижу, что он работает. То есть мы сценариями убедились, что если вдруг у нас в логике есть эта возможность перезапроса, мы это проверили. Вы ручной тестировщик. Вам еще никто ничего не написал, а вы уже можете это проверить, это здорово.
Шаблоны
Еще очень крутая штука в WireMock — возможность использовать шаблоны. Чтобы многие ответы не менять на ходу или если вам при работе вашего сервиса нужно, чтобы какие-то параметры генерировались рандомно или по какой-то логике, у WireMock есть понятие шаблонов.
Здесь, как вы видите, я описываю JSON просто в отдельной строке, где у меня есть какой-то generated_id
, в фигурных скобках я указываю, что это будет рандомное значение из шести цифр. Также сюда можно взять из входящего запроса из query параметра значение someparam
, также используем автогенерируемую дату. Стоит обратить внимание, что здесь прописан дополнительный параметр генерации мока withTransformers
, который указывает, что мы должны использовать шаблоны.
Теперь тот же самый запрос, тот же самый someparam=13
, но в ответ, как вы можете видеть, уже подставился сгенерированный автоматически id, подставилось значение из входящего параметра, а значение можно выбирать из многих вещей: вы можете из заголовков брать значение, можете из структуры входящего запроса выбирать значение, при этом меняя что-то на ходу.
Здесь маленькая интересная вещь, которая пригодится больше автоматизаторам, но я хотел бы про нее рассказать. Мы можем зарегистрировать этот мок с каким-то определенным UUID.
Это больше пригодится для работы с объектом и получения значения через объект в коде, но дальше я вам покажу пример, для чего это нужно.
Работая с объектом мока, вы можете получить все входящие events, которые он обрабатывал. Здесь я накидал какой-то пример, который выводит количество обработанных моком events
, и, оперируя тем же объектом, я вывожу дату, когда был залогирован запрос, количество query параметров и первое значение someparam
.
К примеру, сделав три разных запроса, я получаю в выводе разные даты, одинаковое количество параметров, но, например, видно, что первое значение someparam
меняется. Такие вещи я использовал, когда писал API-автотесты, оперируя объектом, получал обработки каких-то входящих запросов, и мог проверять, присылает мой сервис нужные значения, потому что тестирование бекэнда - вещь такая, не всегда увидишь, что там происходит. А здесь наглядно, оперируя объектом, мы получаем, что наш сервис нам прислал, и можем проверить, то это или нет.
Разберем пример.
private static void setStubWithTemplate2(WireMock wireMock) {
wireMock.removeMappings;
wireMock.resetRequests);
wireMock. resetScenarios;
String urlTostub = "/someurl";
String bodyInResp = "1"
"generated_id": "{{randomValue length=6 type='NUMERIC'}}",
"param_from_query": "{frequest. query - someparam}}",
"date": "{{now}}"
}" "";
wireMock-register(get(urlPathEqualTo(urlToStub))
-withHeader ( key: "Authorization", containing( value: "Basic"))
-willReturn(aResponse)
-withStatus(200)
-withFixedDelay( milliseconds: 100)
-withBody(bodyInResp)
-withHeader ( key: "Content-Type", ...values: "application/json")
-withTransformers ..responseTransformerNames: "response-template")));
}
Как видите, я регистрирую мок, который срабатывает на тот же путь someurl
, будет использовать определенные шаблоны. Здесь привязка регистрации идет к заголовку autorization
, который должен содержать значение Basic
. При такой регистрации мока проверяется наличие Basic-авторизации, и если ваш сервис, делая вызов мока, не будет присылать Basic
, то мок на это не среагирует и вы сразу сможете заметить какие-то ошибки, то есть дополнительная проверка по настройке мока.
Вернемся к входящим запросам и работе, это считайте как работа с вашим сервисом. Да, я делаю запросы Postman’ом, но также вас сервис может приходить и делать запросы WireMock, то есть это просто временно без сервиса, сервисом здесь Postman выступает.
Можно еще раз посмотреть, что у нас два входящих параметра с одинаковыми значениями, я показываю самые простые настройки, поэтому здесь не буду приводить какие-то сложные заголовки и еще что-то, просто база.Можно обратить внимание, 404 не найдено, мы видим в описании, что немного не хватает заголовка.
Я ввожу заголовок authorization
, руками вбиваю тот же Basic с каким-то значением, естественно, в Postman это можно было сделать через вкладку authorization
, ваш сервис будет это делать автоматически. Я выполняю запрос, у нас все совпало, мы получили ответ. Какой-то сгенерированный id, выводим значение первого параметра и автоматически генерируемую дату. По факту вы получили мок, который будет обрабатывать запросы вашего сервиса.
Как теперь вы можете с этим работать? Вот у вас есть настроенный мок, у вас есть сервис, и вам нужно проверить, срабатывает ли какая-то внутренняя логика, если запрос вашего сервиса будет выполняться дольше, чем, допустим, две секунды.
Вы на ходу меняете значение задержки в вашем моке, запускаете выполнение, мок перерегистрировался. Вы опять дергаете свой сервис, чтобы он пришел в мок, и тут уже видно, что он выполнялся две секунды. Вы — ручной тестировщик, все, что вы сделали, — поменяли один параметр, и вот вы можете проверять, обрабатывает ли ваш сервис задержки.
Вроде бы просто прекрасно.
Мы теперь убедились, что две секунды нет запроса, поменяли на пять секунд задержки, обновили моки. Выполнилось, запрос сделали, пять с половиной секунд задержка на ответ, можете обратить внимание.
То есть мы тестируем задержки на самом сервисе, как он с этим работает.
Также там можно настраивать плавающие задержки, по нормали.
Теперь мы хотим посмотреть, как наш сервис обрабатывает разные статусы ответов.
Даже не меняя JSON ответа, мы поменяли на ходу код, я прописал 312, можно прописать вообще любой. Ответ получен, мы можем посмотреть, что есть ошибка 400, что сервис ее обработает правильно, а не свалится.
Вот пример, где яна ходу меняю JSON на совершенно невалидный, прописав просто какие-то цифры, при этом код ответа 200.
Это позволяет вам убедиться, что если ваш сервис получил вообще какую-то белиберду, то он не свалится, утянув за собой все сервисы по соседству.
На живых интеграциях это практически невозможно проверить — вы же не пойдете в команду другого сервиса просить, чтобы они через него отправили какой-то кривой запрос. А тут вы руками за две секунды этим убедились, что ваш сервис обрабатывает все отлично. И поверьте мне, это одна из самых частых ошибок коллег — они не проверяют нестандартные ответы, что часто рушит все. На моей памяти, это одна из самых распространенных ошибок, и если вам за нее делают втык, то, поверьте, не зря. Это вообще детский сад так делать. И недавно у коллег такое нашлось, и они наслушались от меня мало чего хорошего, а я достаточно дружелюбный.
Это были небольшие примеры, как работать, азы, прямо совсем начало, но, поверьте, это далеко не все, что может WireMock. Он умеет сквозь себя проксировать запросы к другим сервисам, то есть если ваш сервис полностью настроен на моки, но какие-то из обращений вы можете спроксировать сильно дальше, получить нужный ответ, но в основном всю логику обработку построить на моках, бывает полезно. У WireMock есть встроенная валидация. Это не когда вы, оперируя объектом, получаете набор всех входящих запросов и по каким-то признакам пытаетесь сами сопоставить в коде, вы также прописываете, что нужно вернуть ответ, было ли обращение к определенному моку с определенными параметрами. Вам просто возвращается True или False, можно этим оперировать.
Вообще сейчас WireMock очень сильно разрастается, читайте документацию, тестируйте приложения для прода, и будете не просто Quality Assurance, а Quality Assistant, потому что вы не только обеспечиваете качество, а помогаете двигать его вперед.