мок сервер что это
Введение в MockServer
1. Обзор
2. Зависимости Maven
Чтобы использовать MockServer в нашем приложении, нам нужно добавить две зависимости:
Последняя версия зависимостей доступна как mockserver-netty и mockserver-client.
3. Функциональность MockServer
Проще говоря, инструмент может:
4. Как запустить MockServer
4.1. Запуск через плагин Maven
Это запустит сервер на этапе тестирования класса процесса и остановится на этапе проверки :
4.2. Запуск через Java API
Мы можем использовать Java API startClientAndServer () для запуска сервера. Обычно мы запускаем сервер перед запуском всех тестов:
5. Имитация клиентов
MockServerClient API используется для обеспечения возможности подключения к MockServer. Он моделирует запросы и соответствующие ответы от сервера.
Он поддерживает несколько операций:
5.1. Создание ожиданий с помощью фиктивных ответов
Чтобы создать ожидание, нам нужно определить сопоставление запросов и ответ, который должен быть возвращен.
Запросы можно сопоставить с помощью:
Все вышеперечисленные параметры можно указать с помощью обычного текста или регулярных выражений.
И ответное действие будет содержать:
Давайте посмотрим, как мы можем создать ожидание :
Здесь мы отправляем запрос POST на сервер. И мы указали, сколько раз нам нужно сделать этот запрос, используя ровно (1) вызов.
Получив этот запрос, мы имитировали ответ с такими полями, как код состояния, заголовки и тело ответа.
5.2. Отправка запроса
Ожидание может быть настроено для пересылки запроса. Несколько параметров могут описать прямое действие:
Давайте посмотрим на пример переадресации запроса:
В данном случае мы смоделировали запрос, который попадет в MockServer ровно один раз, а затем будет перенаправлен на другой сервер. Внешний метод forward () определяет прямое действие, а внутренний вызов метода forward () помогает создать URL-адрес и пересылает запрос.
5.3. Выполнение обратного вызова
Давайте посмотрим на пример ожидания с обратным вызовом:
Здесь внешний callback () указывает действие обратного вызова, а внутренний метод callback () указывает экземпляр класса метода обратного вызова.
В этом случае, когда MockServer получает запрос с / callback, будет выполнен метод обработчика обратного вызова, реализованный в указанном классе:
5.4. Проверка запросов
MockServerClient имеет возможность проверить, отправила ли тестируемая система запрос:
Здесь класс org.mockserver.verify.VerificationTimes используется для указания количества раз, когда Mock Server должен соответствовать запросу.
6. Заключение
В этой быстрой статье мы изучили различные функции MockServer. Мы также изучили различные предоставляемые API и способы их использования для тестирования сложных систем.
Как всегда, полный код этой статьи доступен на GitHub.
Моки и явные контракты
Наверное каждый, кто начинал писать юнит и интеграционные тесты, сталкивался с проблемой злоупотребления моками, которая приводит к хрупким тестам. Последние, в свою очередь, создают у программиста неверное убеждение в том, что тесты только мешают работать.
Ниже представлен вольный перевод статьи, в которой José Valim — создатель языка Elixir — высказал своё мнение на проблему использования моков, с которым я полностью согласен.
Несколько дней назад я поделился своими мыслями по поводу моков в Twitter:
Мок — полезный инструмент в тестировании, но имеющиеся тестовые библиотеки и фреймворки зачастую приводят к злоупотреблению этим инструментом. Ниже мы рассмотрим лучший способ использования моков.
Что такое мок?
Воспользуемся определением из англоязычной википедии: мок — настраиваемый объект, который имитирует поведение реального объекта. Я сделаю акцент на этом позже, но для меня мок — это всегда существительное, а не глагол [для наглядности, глагол mock везде будет переводиться как «замокать» — прим. перев.].
На примере внешнего API
Давайте рассмотрим стандартный пример из реальной жизни: внешнее API.
Представьте, что вы хотите использовать Twitter API в веб-приложении на фреймворке Phoenix или Rails. В приложение приходит запрос, который перенаправляется в контроллер, который, в свою очередь, делает запрос к внешнему API. Вызов внешнего API происходит прямо в контроллере:
Далее вы используете такой же подход в других частях приложения и добиваетесь прохождения юнит и интеграционных тестов. Время двигаться дальше?
Кроме того, так как мок, показанный выше, изменяет модуль глобально, вы больше не сможете запустить эти тесты в Elixir параллельно.
Решение
В Elixir все приложения имеют конфигурационные файлы и механизм для их чтения. Используем этот механизм, чтобы настроить клиент Twitter’a для различных окружений. Код контроллера теперь будет выглядеть следующим образом:
Соответствующие настройки для различных окружений:
Сейчас мы можем выбрать лучшую стратегию получения данных из Twitter для каждого из окружений. Sandbox может быть полезен, если Twitter предоставляет какой-нибудь sandbox для разработки. Наша замоканная версия HTTPClient позволяла избежать реальных HTTP-запросов. Реализация этой же функциональности в данном случае:
Код получился простым и чистым, а сильной внешней зависимости от HTTPClient больше нет. MyApp.Twitter.InMemory является моком, то есть существительным, и для его создания вам не нужны никакие библиотеки!
Необходимость явных контрактов
Мок предназначен для замены реального объекта, а значит будет эффективен только в том случае, когда поведение реального объекта определено явно. Иначе, вы можете оказаться в ситуации, когда мок начнет становиться все сложнее, увеличивая зависимость между тестируемыми компонентами. Без явного контракта заметить это будет сложно.
Мы уже имеем три реализации Twitter API и лучше сделать их контракты явными. В Elixir описать явный контракт можно с помощью behaviour:
Теперь добавьте @behaviour MyApp.Twitter в каждый модуль, который реализует этот контракт, и Elixir поможет вам создать ожидаемый API.
В Elixir мы полагаемся на такие behaviours постоянно: когда используем Plug, когда работаем с базой данных в Ecto, когда тестируем Phoenix channels и так далее.
Тестирование границ
Сначала, когда явные контракты отсутствовали, границы приложения выглядели так:
Поэтому изменение HTTPClient могло приводить к падению интеграционных тестов. Сейчас приложение зависит от контракта и только одна реализация этого контракта работает с HTTP:
Сложность тестирования больших систем заключается в определение четких границ между компонентами. Слишком высокий уровень изоляции при отсутствии интеграционных тестов сделает тесты хрупкими, а большинство проблем будут обнаруживаться только на production. С другой стороны, низкий уровень изоляции увеличит время прохождения тестов и сделает тесты сложными в сопровождении. Единственного верного решения нет, и уровень изоляции будет изменяться в зависимости от уверенности команды и прочих факторов.
Лично я бы протестировал MyApp.Twitter.HTTP на реальном Twitter API, запуская эти тесты по-необходимости во время разработки и каждый раз при сборке проекта. Система тегов в ExUnit — библиотеке для тестирования в Elixir — реализует такое поведение:
Исключим тесты с Twitter API:
При необходимости включим их в общий тестовый прогон:
Также можно запустить их отдельно:
Вместо создания мока HTTPClient можно поднять dummy-сервер, который будет эмулировать Twitter API. bypass — один из проектов, который может в этом помочь. Все возможные варианты вы должны обсудить со своей командой.
Примечания
Я бы хотел закончить эту статью разбором нескольких общих проблем, которые всплывают практически в каждом обсуждении моков.
Создание «тестируемого» кода
Получается, предложенное решение делает production-код более «тестируемым», но создает необходимость ходить в конфигурацию приложения на каждый вызов функции? Наличие ненужного оверхэда, чтобы сделать что-то «тестируемым», не похоже на хорошее решение.
Я бы сказал, что речь идет не о создании «тестируемого» кода, а об улучшении дизайна [от англ. design of your code — прим. перев.].
Тест — это пользователь вашего API, как и любой другой код, который вы пишите. Одна из идей TDD заключается в том, что тесты — это код и ничем не отличаются от кода. Если вы говорите: «Я не хочу делать мой код тестируемым», это означает «Я не хочу уменьшать зависимость между компонентами» или «Я не хочу думать о контракте (интерфейсе) этих компонентов».
Нет ничего плохого в нежелании уменьшать зависимость между компонентами. Например, если речь идет о модуле работы с URI [имеется ввиду модуль URI для Elixir — прим. перев.]. Но если мы говорим о чем-то таком же сложном, как внешнее API, определение явного контракта и наличие возможности заменять реализацию этого контракта сделает ваш код удобным и простым в сопровождении.
Кроме того, оверхэд минимален, так как конфигурация Elixir-приложения хранится в ETS, а значит вычитывается прямо из памяти.
Локальные моки
Хотя мы и использовали конфигурацию приложения для решения проблемы с внешним API, иногда проще передать зависимость как аргумент. Например, некоторая функция выполняет долгие вычисления, которые вы хотите изолировать в тестах:
Можно избавиться от зависимости, передав её как аргумент. В данном случае будет достаточно передачи анонимной функции:
Тест будет выглядеть следующим образом:
Или, как было описано ранее, можно определить контракт и передать модуль целиком:
Вы также можете представить зависимость в виде data structure и определить контракт с помощью protocol.
Мок — это существительное
Лучше думать о моках как о существительных. Вместо того, чтобы мокать API (мокать — глагол), нужно создать мок (мок — существительное), который реализует необходимый API.
Библиотеки для создания моков
После прочитанного у вас может возникнуть вопрос: «Нужно ли отказываться от библиотек для создания моков?»
Все зависит от ситуации. Если библиотека подталкивает вас на подмену глобальных объектов (или на использование моков в качестве глаголов), изменение статических методов в объектно-ориентированном или замену модулей в функциональном программировании, то есть на нарушение описанных выше правил создания моков, то вам лучше отказаться от неё.
Однако, есть библиотеки для создания моков, которые не подталкивают вас на использование описанных выше анти-паттернов. Такие библиотеки предоставляют «мок-объекты» или «мок-модули», которые передаются в тестируемую систему в качестве аргумента и собирают информацию о количестве вызовов мока и о том, с какими аргументами он был вызван.
Заключение
Одна из задач тестирования системы — нахождение правильных контрактов и границ между компонентами. Использование моков только в случае наличия явного контракта позволит вам:
Явные контракты позволяют увидеть сложность зависимостей в вашем приложении. Сложность присутствует в каждом приложении, поэтому всегда старайтесь делать её настолько явной, насколько это возможно.
Когда использовать mocks в юнит-тестировании
Эта статья является переводом материала «When to Mock».
Использование моков в модульном тестировании является спорной темой. Автор оригинала заметил, что на протяжении всей своей карьеры в программировании он сначала перешел от моков почти над каждой зависимостью к политике «без моков», а затем к «только моки для внешних зависимостей».
Ни одна из этих практик не является достаточно хорошей. В этой статье Владимир Хориков покажет, какие зависимости следует мокать, а какие использовать как есть в тестах.
Что такое mock (мок, от англ. «пародия», «имитация»)?
Прежде чем перейти к теме того, когда использовать моки, давайте обсудим, что такое мок. Люди часто используют термины тестовый двойник (test double) и мок (mock) как синонимы, но технически это не так:
Мок – это лишь один из видов таких зависимостей.
Согласно Жерару Месарошу, существует 5 видов тестовых двойников:
Такое разнообразие может показаться пугающим, но на самом деле все они могут быть сгруппированы всего в два типа: моки и стабы.
Разница между этими двумя типами сводится к следующему:
Моки помогают имитировать и изучать исходящие (outcoming) взаимодействия. То есть вызовы, совершаемые тестируемой системой (SUT) к ее зависимостям для изменения их состояния.
Стабы помогают имитировать входящие (incoming) взаимодействия. То есть вызовы, совершаемые SUT к ее зависимостям для получения входных данных.
Извлечение данных из БД является входящим (incoming) взаимодействием — оно не приводит к побочному эффекту. Соответствующий тестовый двойник является стабом.
Все остальные различия между пятью типами тестовых двойников являются незначительными деталями реализации:
Spies (шпионы) выполняют ту же роль, что и моки. Отличие в том, что spies пишутся вручную, а моки создаются с помощью готовых инструментов. Иногда spies называют рукописными моками.
С другой стороны, разница между стабами, dummy (пустышками) и фейками заключается в том, насколько они умны:
Стаб посложнее. Это полноценная зависимость, которую вы настраиваете для возврата разных значений для разных сценариев.
Обратите внимание на разницу между моками и стабами (помимо исходящих и входящих взаимодействий). Моки помогают эмулировать и изучать взаимодействия между SUT и его зависимостями, в то время как стабы помогают только эмулировать эти взаимодействия. Это важное различие.
Мок-как-инструмент vs. мок-как-тестовый-двойник
Но у термина мок есть и другое значение. Вы также можете ссылаться на классы из библиотек (для создания моков) как на моки. Эти классы помогают вам создавать настоящие моки, но сами по себе они не являются моками как таковыми:
Важно не смешивать мок-как-инструмент с мок-как-тестовый-двойник, потому что вы можете использовать мок-как-инструмент для создания обоих типов тестовых двойников: моков и стабов.
Не проверяйте взаимодействия со стабами
Как уже упоминал выше, моки помогают эмулировать и изучать исходящие взаимодействия между SUT и его зависимостями, в то время как стабы помогают только эмулировать входящие взаимодействия, а не изучать их.
В приведенных выше примерах проверка
В то же время вызов GetNumberOfUsers() вообще не является результатом. Это внутренняя деталь реализации, касающаяся того, как SUT собирает данные, необходимые для создания отчета. Следовательно, проверка этого вызова приведет к уязвимости теста. Неважно, как SUT генерирует конечный результат, если этот результат правильный.
Вот пример такого хрупкого теста:
Такая практика проверки того, что не является частью конечного результата, также называется чрезмерной спецификацией.
Совместное использование моков и стабов
Иногда нужно создать тестовый двойник, который проявляет свойства как мока, так и стаба:
Этот тест использует storeMock для двух целей: он возвращает шаблонный ответ и проверяет вызов метода, сделанный SUT.
Однако обратите внимание, что это два разных метода: тест устанавливает ответ от HasEnoughInventory(), но затем проверяет вызов RemoveInventory(). Таким образом, здесь не нарушается правило не проверять взаимодействия со стабами.
Mocks vs. stubs и commands vs. queries
Понятие моков и стабов связано с принципом command-query separation (CQS). Принцип CQS гласит, что каждый метод должен быть либо командой, либо запросом, но не обоими:
Другими словами, задавая вопрос, вы не должны менять ответ. Код, который поддерживает такое четкое разделение, становится легче для чтения. Тестовые двойники, заменяющие команды, становятся моками. Аналогично, тестовые двойники, заменяющие запросы, являются стабами:
Посмотрите еще раз на два теста из предыдущих примеров:
Когда мокать?
Разобравшись со всеми этими определениями, давайте поговорим о том, когда вам следует использовать моки.
Очевидно, что вы не хотите мокать саму тестируемую систему (SUT), поэтому вопрос «Когда мокать?» сводится к следующему: «Какие типы зависимостей вы должны заменять на моки, а какие использовать в тестах?»
Вот все типы зависимостей модульного тестирования, которые автор оригинала перечислил в своей предыдущей статье:
Совместная зависимость соответствует изменяемой внепроцессорной зависимости (mutable out-of-process dependency) в подавляющем большинстве случаев, поэтому автор оригинала использует здесь эти два понятия как синонимы. (Ознакомьтесь с предыдущим постом Владимира Хорикова, чтобы узнать больше: Unit Testing Dependencies: The Complete Guide.)
Существуют две школы модульного тестирования с собственными взглядами на то, какие типы зависимостей следует заменять на моки:
Лондонская школа (также известная как школа mockist) выступает за замену всех изменяемых зависимостей на моки.
Классическая школа (также известная как школа Детройта) выступает за замену только общих (изменяемых внепроцессорных) зависимостей.
Обе школы ошибаются в своем отношении к мокам, хотя классическая школа меньше, чем лондонская.
Моки и неизменяемые внепроцессорные зависимости.
А как насчет иммутабельных внепроцессорных (immutable out-of-process) зависимостей? Разве их не стоит мокать, по крайней мере, по мнению одной из школ? Неизменяемые внепроцессорные зависимости (например, служба API только для чтения) следует заменить тестовым двойником, но этот тестовый двойник будет стабом, а не моком.
Это опять же из-за различий между моками и стабами:
Взаимодействия с иммутабельными внепроцессорными зависимостями по определению являются входящими и, следовательно, не должны проверяться в тестах, а только заменяться шаблонными ответами (обе школы с этим согласны).
Не мокайте все изменяемые зависимости
Не стоит мокать все изменяемые зависимости. Чтобы понять, почему, нам нужно рассмотреть два типа коммуникаций в типичном приложении: внутрисистемный и межсистемный.
Внутрисистемные коммуникации являются деталями реализации, поскольку взаимодействие, через которое проходят ваши доменные классы для выполнения операции, не является частью их наблюдаемого поведения. Это взаимодействие не имеет непосредственной связи с целью клиента. Таким образом, связь с таким взаимодействием приводит к хрупким тестам.
Это свойство межсистемных коммуникаций проистекает из того, как отдельные приложения развиваются вместе. Один из основных принципов такой эволюции – обеспечение обратной совместимости. Независимо от рефакторинга, который вы выполняете внутри своего приложения, паттерн взаимодействия, который оно использует для взаимодействия с внешними приложениями, должен всегда оставаться на месте, чтобы внешние приложения могли его понять. Например, сообщения, отправляемые вашим приложением по шине, должны сохранять свою структуру и так далее.
Использование моков закрепляет контракт взаимодействия между тестируемой системой и зависимостью. Это именно то, что вам нужно при проверке связи между вашей системой и внешними приложениями. И наоборот, использование моков для проверки связи между классами внутри вашей системы связывает ваши тесты с деталями реализации, делая их хрупкими. Внутрисистемные связи соответствуют изменяемым внутрипроцессорным зависимостям:
Итак, лондонская школа ошибается, потому что она поощряет использование моков для всех изменяемых зависимостей и не проводит различия между внутрисистемными (внутрипроцессорными) и межсистемными (внепроцессорными) коммуникациями.
В результате тесты проверяют связь между классами точно так же, как они проверяют связь между вашим приложением и внешними системами. Это неизбирательное использование моков является причиной того, что следование лондонской школе часто приводит к хрупким тестам — тестам, которые связаны с деталями реализации.
Не мокайте все внепроцессорные зависимости
Классическая школа лучше справляется с этим вопросом, потому что она выступает за замену только внепроцессорных зависимостей, таких как служба SMTP, шина сообщений и т. д. Но классическая школа также не идеальна по отношению к межсистемным коммуникациям. Эта школа также поощряет чрезмерное использование моков, хотя и не так сильно, как лондонская школа.
Не все внепроцессорные зависимости следует мокать. Если внепроцессорная зависимость доступна только через ваше приложение, то связь с такой зависимостью не является частью наблюдаемого поведения вашей системы. Внепроцессорная зависимость, которая не может наблюдаться извне, по сути, действует как часть вашего приложения. Связи с такой зависимостью становятся деталями реализации: они не должны оставаться неизменными после рефакторинга и, следовательно, не должны проверяться с помощью моков.
Помните, что требование всегда сохранять схему взаимодействия между вашим приложением и внешними системами проистекает из необходимости поддерживать обратную совместимость. Вы должны поддерживать способ взаимодействия вашего приложения с внешними системами. Это потому, что вы не можете изменять эти внешние системы одновременно с вашим приложением; они могут следовать другому циклу развертывания, или вы можете просто не контролировать их.
Но когда ваше приложение действует как прокси-сервер для внешней системы, и ни один клиент не может получить к ней прямой доступ, требование обратной совместимости исчезает. Теперь вы можете развернуть свое приложение вместе с этой внешней системой, и это не повлияет на клиентов. Схема взаимодействия с такой системой становится деталью реализации.
Хорошим примером здесь является БД приложения: БД, которая используется только вашим приложением. Ни одна внешняя система не имеет доступа к этой БД. Таким образом, вы можете изменить схему взаимодействия между вашей системой и БД приложения любым удобным вам способом, если это не ломает существующую функциональность. Поскольку эта БД полностью скрыта от глаз клиентов, вы даже можете заменить ее совершенно другим механизмом хранения, и никто этого не заметит.
Использование моков для внепроцессорных зависимостей, которые вы полностью контролируете, также приводит к хрупким тестам. Вы не хотите, чтобы ваши тесты становились красными каждый раз, когда вы разбиваете таблицу в БД или изменяете тип одного из параметров в хранимой процедуре. БД и ваше приложение должны рассматриваться как одна система.
Это различие разделяет внепроцессорные зависимости на две подкатегории:
Управляемые (managed) зависимости — внепроцессорные зависимости, над которыми вы имеете полный контроль. Эти зависимости доступны только через ваше приложение; взаимодействия с ними не видны внешнему миру. Типичным примером является БД приложения. Внешние системы не имеют прямого доступа к вашей БД; они делают это через API, предоставляемый вашим приложением.
Только неуправляемые зависимости должны быть заменены моками. Используйте реальные экземпляры управляемых зависимостей в тестах.
Резюме
Существует пять вариантов тестовых двойников — dummy (манекен), стаб, spy (шпион), мок и фейк, которые можно сгруппировать всего в два типа: моки и стабы.
Spies функционально такие же, как и моки; dummy и фейки выполняют ту же роль, что и стабы.
Различия между моками и стабами:
Моки помогают имитировать и изучать исходящие взаимодействия: вызовы от SUT к его зависимостям, которые изменяют состояние этих зависимостей.
Стабы помогают имитировать входящие взаимодействия: для получения входных данных SUT обращается к своим зависимостям.
Проверка взаимодействия со стабами всегда приводит к хрупким тестам.
Тестовые двойники, заменяющие команды CQS, являются моками. Тестовые двойники, заменяющие запросы CQS, являются стабами.
Внепроцессорные зависимости можно разделить на 2 подкатегории: управляемые и неуправляемые зависимости.
Используйте реальные экземпляры управляемых зависимостей в интеграционных тестах; замените неуправляемые зависимости моками.
Заглушки для веб-сервисов
Приложения 1С редко работают в изоляции — чаще всего они интегрируются с различными сервисами, которые могут быть расположены как в локальной сети предприятия, так и на удаленных серверах. И иногда встает вопрос тестирования этой интеграции в рамках общего сценарного тестирования. Посмотрим, с какими проблемами при этом можно столкнуться и при чем тут вообще эти самые «заглушки».
Содержание
С чем будем работать
Давайте предположим, что мы ведем разработку функционала, который обращается к удаленному веб-сервису, получает из него данные и запускает в базе 1С механизм расчетов. Не важно какие именно данные возвращает сервис: важно, то, что полученный результат прямо влияет на работу наших алгоритмов.
Предположим, что веб-сервис отвечает на запросы с ощутимым задержками, потому что обращается в нагруженную базу данных в реальном времени. При этом он расположен в VPN-сети и физическое его нахождение нам неизвестно: сервер может находиться в соседнем здании, может быть поднят в облаке или физически располагаться в зарубежном дата-центре.
В целом, используется стандартная схема: 1С обращается к сервису, который опубликован на веб-сервере; веб-сервис обращается к базе данных и возвращает результат.
Мы уже используем сценарное тестирование и хотим, чтобы наши тесты работали стабильно и не зависели от доступности удаленного сервера, загруженности серверного оборудования или от неполадок в сети. Поскольку прогон тестов — процесс не однократный, а регулярный (и в целом автоматический), мы хотим сделать так, чтобы в момент запуска тестов веб-сервис был всегда доступен и работал максимально быстро, стабильно и предсказуемо.
Обратите внимание, что мы не собираемся проверять работоспособность веб-сервиса и заниматься его тестированием — этим занимаются разработчики веб-сервиса. Нам нужно обеспечить его бесперебойную работу на момент проведения тестирования.
Получается, нам нужна такая «локальная копия» удаленного сервиса, которую можно настроить нужным нам образом, а своему приложению сказать: «Во время тестирования не ходи на удаленный сервер, а ходи на localhost».
Проблемы тестирования интеграции
Вообще, для использования заглушек веб-сервисов может быть много причин. Например, следующих:
Тесты замедляются. Если поставщик сервиса находится далеко, сетевая среда нестабильная и при вызове сервиса происходит задержка, то время прохождения тестов может значительно увеличиваться.
Тесты работают нестабильно. Веб-сервисы могут быть не всегда доступны из-за технических обновлений, подвержены перегрузкам или ошибкам сетевых протоколов.
Тесты покрывают не все возможные варианты ответов сервиса. Не всегда есть возможность получить некоторые ответы от реального веб-сервиса и промоделировать все рабочие ситуации — следовательно, максимально полно протестировать взаимодействие.
Доступ к рабочим веб-сервисам ограничен. Часто встречается ситуация, когда рабочие сервисы недоступны из тестового контура, в котором программисты ведут разработку. Это делает как по причине безопасности, так и для того, чтобы не подвергать лишней нагрузке рабочее окружение.
Из-за ошибок разработчика могут быть подвергнуты риску реальные данные. Например, есть веб-сервисы, которые позволяют добавлять или изменять данные в удаленной системе. Ошибка при вызове такого сервиса может привести к потере данных.
Давайте теперь поближе рассмотрим мок-объекты.
Заглушки
Существует несколько видов объектов, которые позволяют симулировать поведение реальных объектов во время тестирования:
Относительно веб-сервисов можно сказать, что мок-сервис позволяет переопределить реальный сервис и подставить вместо него упрощенную реализацию, которая работает нужным разработчику образом и дает доступ к собственным данным и настройкам. Мы как бы создаем у себя аналог удаленного веб-сервиса, который отвечает на вызовы нашего приложения.
Правда, тут возникают две сложности.
Во-первых, моки надо создать. Надо устанавливать дополнительный софт, описывать логику, загружать нужные данные в макет ответа. Не то чтобы это сложно, но это требует усилий и времени. Может быть, кому-то будет проще сохранить результат вызова в файл и при тестировании обращаться не к заглушке, а к файлу. В небольших проектах с малым количеством тестов такой подход вполне оправдан.
Во-вторых, веб-сервис может поменяться. Например, программист написал рабочую логику для заглушки, а через какое-то время веб-сервис стал работать по-другому (а разработчика, как часто бывает, об этом никто не уведомил). О том, что в работе веб-сервиса что-то поменялось он узнал, когда выкатил обновление на рабочую базу.
Решение простое — для сервиса, который может быть подвержен изменениям нужно сделать набор дымовых тестов (smoke-тесты). Дымовые тесты проверяют, что основные методы сервиса не изменились и обращение к ним не вызывает ошибок.
Мы рассмотрим способ создания мок-сервисов в программе SoapUI, которая является достаточно популярной в мире тестирования. Первоначальная схема работы примет такой вид:
Тестируемое приложение 1С больше не будет обращаться к удаленному сервису, а получит тот ответ, который мы заранее подготовили и опубликовали из SoapUI.
Запускаем моки через SoapUI
Шаг 0. Готовим окружение
Программа SoapUI (подробнее о ней можно прочитать в публикации Использование SoapUi для работы с веб-сервисами. Часть1) позволяет на основании WSDL-схемы сервиса создать Mock-service и опубликовать его на собственном веб-сервере, который умеет запускать.
SoapUI можно получить на официальном сайте компании SmartBear. Для наших целей достаточно бесплатной 32х-разрядной OpenSource версии — ее нужно скачать и установить.
В конфигурации 1С, которую мы дорабатываем, есть обработка Расчет трафика — она обращается к веб-сервису CalculateService и вызывает метод CalculateTraffic, в который передает несколько параметров, вроде ID, StoreID и других. Рассчитанный веб-сервисом результат записывается в регистр и запускает внутренний механизм расчетов (что он именно делает, в рамках статьи значения не имеет).
Мы дорабатываем механизм расчетов и во время проведения тестов хотим вместо ответов удаленного сервиса подставлять свои заранее заготовленные варианты.
! Поскольку я воспроизвожу пример для статьи на своем компьютере, в адресе вы видите «localhost». Но подразумевается, что в рабочей ситуации мы будем обращаться к рабочему сервису на рабочем сервере.
Для начала запустим браузер и убедимся, что веб-сервис доступен:
Дальше добавим веб-сервис в SoapUI и сделаем для него заглушку.
Шаг 1. Импортируем WSDL-схему
Запустим SoapUI и создадим новый проект.
Если при импорте возникает ошибка, дело скорее всего в авторизации: нужно ввести имя пользователя и пароль.
Забегая немного вперед, добавлю, что в SoapUI есть удобный способ сохранить данные авторизации: нужно заполнить параметры Username и Password на вкладке Auth, которая станет доступна в редакторе запроса к сервису:
В дереве проекта появился веб-сервис CalculateService.
Теперь мы можем заполнить параметры и обратиться к сервису:
Шаг 2. Запускаем mock-сервис
Задаем параметры MockService:
Обратите внимание на порт, на котором будет доступен сервис и на путь к нему.
Жмем ОК и заполняем имя мок-сервиса:
Открывается окно, из которого можно запустить мок-сервис и указать его настройки:
Зайдем в настройки и укажем, что мок будет подниматься на локальном интерфейсе localhost:
Жмем на Запуск. Если запуск пройдет успешно, станет доступна кнопка Открыть WSDL-схему в браузере:
В дереве проекта появился мок-сервис MoskCalculateService с методом CalculateTraffic:
Шаг 3. Настраиваем ответ
Открываем редактор ответа Response 1. В этой форме можно определить данные, которые будет возвращать наш мок-сервис.
Здесь можно заполнить значения полей ответа, подставить любую валидную XML или написать скрипт на Groovy, который будет определять содержание ответа в зависимости от запроса к сервису. Например, мы можем в получать в скрипте значения переданных параметров, считывать файлы с диска и возвращать их клиенту с результатом вызова, при это логгируя все входящие запросы.
Я покажу простой вариант: получаем из входящего запроса значения параметров StoreID и Offset, сам запрос сохраняем на диск, а клиенту возвращаем значение, рассчитанное по формуле Offset*24 + StoreID:
Я сохранил проект в d:\BASE1C\DEMO\MockCalculateService.xml
Запускаем проект SoapUI из консоли
Для того чтобы поднять мок-сервис из командной строки (а, следовательно, иметь возможность сделать это программным образом), нужно выполнить файл mockservicerunner.bat из директории C:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin.
Подробное описание параметров запуска mockservicerunner.bat можно найти на странице сайте SoapUI или в консоли:
Главный обязательный параметр — путь к xml-файлу с проектом из SoapUI. Программа считывает настройки проекта из файла и по умолчанию поднимает все мок-сервисы, которые в нем описаны.
Открываем консоль в папке c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\ и запускам наш сервис командной:
В консоли наблюдаем, что MockService started on port 8090:
Подробный лог запуска записывается в c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\soapui.log, а ошибки можно найти в файлах soapui-errors.log и errors.log
При запуске может возникнуть такая ошибка:
2019-03-04 11:30:05,556 ERROR [errorlog] java.lang.ClassNotFoundException: com.eviware.soapui.plugins.auto.factories.AutoImportMethodFactory
Проверим еще раз, что WSDL-схема доступна по адресу http://localhost:8090/MockCalculateService?WSDL.
Теперь мы умеем запускать заглушки с помощью командной строки. Пойдем дальше и попробуем прикрутить все это дело к Ванессе.
Подключаем Ванессу
Поскольку на Инфостарте уже есть замечательные статьи про тестирование и применение Ванессы на практике, сразу перехожу к делу.
План такой: при запуске теста будем поднимать наш мок-сервис, проверять его доступность, заполнять поля на форме и нажатие на кнопку Рассчитать выызвать его метод CalculateTraffic.
Определим примерный набросок сценария:
Поднимаем сервис из файла;
Проверяем, что сервис доступен;
Заполняем параметры на форме обработки;
Выполняем вызов процедуры, которая обращается к сервису;
Проверяем результат расчета;
Завершаем работу мок-сервиса.
Создадим файл ПроверкаСервиса.feature (я взял за основу одну из своих существующих фич) и загрузим его в Ванессу:
Дальше мы будем заниматься реализацией шагов. Итоговый текст фичи будет приведен в конце.
Подготовка данных
Пара слов о том, как будем хранить настройки для обращений к веб-сервисам.
Полный путь к файлу mockservicerunner.bat записываем в константу ПутьКЗапускателюЗаглушекВебСервисов, а для описаний веб-сервисов добавим справочник ВебСервисы:
При выполнении сценария каждый раз будем заполнять константу:
И добавлять новый элемент в справочник Веб-сервисы:
Делаем это потому, что другой тест или пользователь может перезаписать или удалить наши значения — то есть нет гарантии, что во время тесты в них будут заполнены правильном образом.
Элемент справочника будем загружать из макета. Так удобнее, чем генерировать заполнение полей через кнопко-нажималку.
В сгенерированной Ванессой обработке (этап генерации обработки я опускаю, подразумевая, что делать это мы умеем) нужно добавить макет и скопировать туда содержимое табличного документа:
Запускаем моки
Для запуска моков нам нужно выполнить батник mockservicerunner.bat с параметром d:\BASE1C\DEMO\MockCalculateService.xml.
Видим, что в стандартной библиотеке Ванессы есть шаг Я Запускаю команду с параметром, который позволяет выполнить команду систему с произвольным набором параметров.
Можно было бы воспользоваться этим шагом, но, как говорится — есть нюансы.
Во-первых, придется каждый раз писать что-то вроде «Я запускаю команду ‘c:\Program Files (x86)\SmartBear\SoapUI-5.5.0\bin\mockservicerunner.bat’ с параметром «d:\BASE1C\DEMO\MyServiceProject.xml» — это длинно и неудобно.
Во-вторых, библиотечный шаг выполняется только в синхронном режиме — то есть вызывающий процесс (в нашем случае это 1С) будет ожидать завершения работы вызываемой команды, чтобы продолжить свое выполнение. Это значит, что 1С запустит мок-сервис и будет висеть, пока он будет запущен. Не совсем то, что нам нужно.
Вот так реализован шаг Я запускаю команду в файле из поставки libraries\Плагины\step_definitions\Фича_УправлениеПриложениями.epf:
А вот реализация функции Выполнить команду ОС без показа черного окна в одной из главных обработок Ванессы bddRunner.epf:
Видно, что вызывается метод Run() объекта WScript.Shell и параметр ЖдатьОкончания вроде бы есть, но передать значения в него нельзя. Баг это или фича, честно говоря, я не понял 🙂 Удобнее всего для запуска моков создать свой шаг, в котором можно принудительно указать параметр метода Run().
Реализуем шаг Я запускаю моки из файла:
Ожидание запуска моков
Чтобы сделать цикличную проверку доступности сервисов, используем функцию ПодключитьОбработчикОжидания и функцию Ванессы ПродолжитьВыполнениеШагов:
Остановка мок-сервисов
После того как тестируемая программа выполнила обращение к мок-сервису, нужно остановить его работу.
При использовании графического интерфейса SoapUI все просто: для остановки мок-сервиса нужно на красную кнопку Stop the mock service.
При запуске через командную консоль тоже проблем нет — достаточно нажать сочетание клавиш Ctrl+C (или Ctrl+Z).
А в случае, когда мы запускаем процесс через WScript.Shell, все несколько сложнее — штатного способа остановить выполнение его нет.
Например, вот вопрос в официальном сообществе SoapUI, который так и остался без ответа: Stop mock service using mockServiceRunner.
Вопрос достаточно распространенный и разработчики предлагают разные решения:
Запоминать PID (идентификатор) процесса и «пристреливать» его после завершения теста. Пример для Linux: Sample script to start/stop soapui mockservicerunner with nohup
Сделать обертку над SoapUI, которая после запуска веб-сервиса будет пытаться прочитать внешний файл-флаг. Если файла нет — все ок, работаем дальше. Если файл появился — завершаем работу сервиса. Проект на гитхабе: Wrapper to start SoapUI MockService Runner
Написать собственное приложение или скрипт, который будет парсить файл проекта SoapUI, извлекать из него описание сервиса и поднимать собственный веб-сервис.
Пока в публичном доступе готового решения для Windows не найдено, остановимся на простом варианте — будем завершать работу процесса java.exe командой
это сработает, если больше нет запущенных процессов java.exe (точнее говоря, они все будут завершены в момент выполнения этой команды).
Или можно использовать более продвинутый вариант:
скрипт завершит работы всех процессов, у которых в параметрах запуска есть название нашего файла с проектом: «MyServiceProject.xml».
Коллеги, если у кого есть более изящное решение, сообщите 🙂
Результат
Теперь мы можем составить полный текст нашего сценария «Расчет трафика»: