Модульное тестирование Pega-приложений: меняем жизнь к лучшему с Ninja

в 10:37, , рубрики: Pega, Блог компании ГК ЛАНИТ, корпоративные приложения, тестирование, Тестирование IT-систем, Тестирование веб-сервисов, метки:

Несмотря на парадигму ‘no coding’, модульное тестирование на сложных проектах Pega так же важно, как и на других проектах по разработке ПО. В этом я убедился лично, работая в проектах по сквозной автоматизации бизнес-процессов на базе решений Pegasystems.

На Хабре я нашел всего одну статью, посвященную платформе Pega. А между тем Pega ежегодно получает высокие оценки в самых авторитетных рейтингах BPM-решений и CRM-приложений.

Развивая тему работы на Pega, предлагаю вам перевод своей статьи о Ninja – инструменте для тестирования приложений Pega. По ходу комментирую терминологию, которую использовал в этом материале.

Модульное тестирование Pega-приложений: меняем жизнь к лучшему с Ninja - 1

В 2015 году я участвовал в автоматизации процесса кредитования корпоративного блока на платформе Pega в одном из крупнейших финансовых институтов России и Восточной Европы.

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

Однажды что-то пошло не так

Чтобы вы могли представить масштаб системы, скажу, что к февралю этого года она достигла размера более 32 тысяч рулов (rule – единица построения приложения на платформе Pega, в Java это может соответствовать методу/классу, но аналогия грубая). Сейчас ее развивают три независимые команды, которые производят более четырех тысяч чекинов в день.

В 2015 году мы вместе с коллегами из «ЛАНИТ – Би Пи Эм» завершали очередной этап проекта и готовили релиз, содержащий множество сложных интеграционных взаимодействий с back-end системами заказчика.

Проблемы начались неожиданно. Системное тестирование выявило ряд ошибок в интеграционных сценариях.

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

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

Список ближайших задач напоминал обычную проектную рутину. Нужно было:

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

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

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

Модульное тестирование Pega-приложений: меняем жизнь к лучшему с Ninja - 2
Источник

Анализ проблемы

Мы проанализировали этот болезненный провал и выделили следующие основные причины.

  • Из-за высокой сложности приложения невозможно было проверить, что изменения, внесенные в одном месте, не ломают что-нибудь в совершенно другом (абсолютно неожиданном) месте. Это стало причиной большого количества регрессионных дефектов.
  • Описание компонентов приложения, которые мы вели на wiki проекта, не были привязаны к коду и часто были неактуальны – не описывали специфичное поведение рулов при определённых условиях. Таким образом, разработчики при использовании существующих компонентов не обладали достаточной информацией о том, как обрабатывать все возможные исключения или специфичные возвращаемые значения.
  • Разработчики фокусировались на обработке основных сценариев, оставляя не реализованными альтернативные ветки, обработку ошибок или поведение при отсутствии данных.

С учетом нашего опыта в Java мы прекрасно понимали, что подобные проблемы в Java-проектах обычно решаются с помощью известных практик: модульного тестирования, test-driven development и continuous integration. Нам показалось логичным применить данные практики в нашем Pega-проекте.

Проектирование модульных тестов

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

Типовой интеграционный компонент нашего приложения состоял из 6 основных элементов.

  • Connect activity – основной элемент, который объединял все остальные элементы и служил точкой входа для вызова интеграции из различных частей приложения.
  • Request mapping data transform заполнял запрос на основе бизнес-данных.
  • Stream XML преобразовывал запрос из интеграционной модели в XML.
  • Connector rule взаимодействовал с внешней системой по требуемому протоколу.
  • Parse XML разбирал XML ответа и преобразовывал его в интеграционную модель данных.
  • Response mapping data transform преобразовывал ответ из интеграционной модели в бизнес-сущности.

Stream и Parse XML рулы обычно создавались автоматически с использованием Connector and Metadata Wizard вместе и интеграционной моделью данных. Connector рул специально оставлялся «тонким» за счёт перемещения всей логики в Connect activity. Наибольшее количество дефектов порождалось Connect activity и Request/Response mapping data transform, т.к. они содержали основную логику интеграционного компонента.

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

Модульное тестирование Pega-приложений: меняем жизнь к лучшему с Ninja - 3

  • Тест Connect activity в изоляции для проверки её логики, включая нестандартные ситуации.
  • Тест Request mapping data transform в изоляции.
  • Тест Response mapping data transform в изоляции.
  • Тест на весь интеграционный компонент с целью проверки, что исходящий XML формируется, а входящий XML обрабатывается корректно (в соответствии со спецификацией).

Элементы на диаграмме раскрашены в соответствии с «забагованностью» и желаемой степенью покрытия тестами:

  • красный – элементы, в значительной степени подверженные ошибкам, должны тестироваться в изоляции для достижения большего покрытия (модульные тесты);
  • зеленый – элементы, менее подверженные ошибкам, при тестировании их достаточно проверить в связке с другими элементами (речь об inner-com тестах, то есть тестах внутреннего компонента в изоляции от внешних систем);
  • серый – компоненты, которые вряд ли могут содержать ошибки и могут оставаться не покрытыми пакетом тестов.

Модульное тестирование средствами платформы Pega

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

В нашем случае это означало, что мы не сможем выполнять тестирование в изоляции не только для Connect activity, но также и для Data transform, т.к. они могут использовать (и часто используют) Data Pages, которые, в свою очередь, используют другие рулы в качестве источника данных (Activity, Report Definitions, другие Data Transform и т.д.).

Таким образом, модульное тестирование от Pega подходило для реализации только одного из четырех типов тестов – inner-com тестов. Более того, это было возможно только благодаря механизму Integration Simulation, который, в свою очередь, создавал проблемы части гибкости и сопровождения тестов.

Описанное выше ограничение было не единственным, что нас беспокоило в модульных тестах от Pega.

  • До выхода Pega 7.2 (в тот момент мы использовали Pega 7.1.8) не было удобного способа управления подготовкой данных для теста и проверкой результатов. Побочным эффектом данного ограничения была необходимость пересоздания всех тестов после существенных изменений рулов.
  • Низкая степень повторного использования кода тестов: даже с учетом существенных улучшений в Pega 7.2, нет возможности разбить комплексные проверки результатов теста на отдельные блоки и использовать их в нескольких сценариях.
  • Нет возможности писать комплексные тестовые сценарии, включающие вызов нескольких рулов и проверку общего результата их работы.
  • Pega предоставляет поддержку модульного тестирования для ограниченного набора рулов (Data Pages, Activity, Decision Table, Decision Tree, Flow, Service SOAP).

С учетом всех этих проблем мы определенно нуждались в альтернативном подходе к автоматизированному тестированию.

Research & Development

Мы начали с подбора сильной команды ветеранов из Java и Pega проектов. Через мозговые штурмы и отзывы от проектных команд мы добились кристально чистого понимания того, какие функции нам нужны. Это:

  • управление изоляцией рулов;
  • Mock и Stub для рулов (аналогично Mock и Stub для классов в Java);
  • декомпозиция сложных тестов на маленькие, повторно используемые блоки;
  • запуск модульных тестов на любом современном build-сервере.

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

Приблизительно в течение года мы внедрили новый подход к тестированию на большинстве наших Pega-проектов. Созданному инструменту требовалось название, и оно появилось само собой – Ninja.

Модульное тестирование Pega-приложений: меняем жизнь к лучшему с Ninja - 4
Источник

Модульное тестирование с Ninja

Ninja предоставляет Java библиотеку, позволяющую писать JUnit тесты для рулов. Таким образом, модульное тестирование приложения на платформе Pega становится таким же простым, как в Java.

Давайте немного подробнее остановимся на том, что делает Ninja таким удобным.

Во-первых, Ninja шифрует и безопасно хранит ваши учетные данные для подключения к среде разработки Pega (представляет собой web-приложение, известное как Pega Designer Studio) и выполнения тестов от вашего имени. Это позволяет тестировать приватную версию рула, находящуюся в checkout. Здесь на всякий случай поясню: платформа Pega позволяет брать рулы в checkout и вносить в них изменения, «видимые» только автору. После того как автор проверил внесенные изменения, он выполняет check-in. Затем изменения становятся «видны» всем.

Во-вторых, Ninja использует одну и ту же сессию для последовательных запусков теста. Это позволяет определить Requestor (сессию работы с системой), используемый Ninja, и подключиться к нему с помощью Tracer – инструмента для отладки в платформе Pega, который позволяет просматривать детальную информацию о выполняемых шагах и состоянии объектов в памяти. В данном случае Tracer необходим для анализа происходящего при выполнении тестируемого рула.

Таким образом, Ninja позволяет готовить тестовое окружение легко и точно.

final String myClass = "MyOrg-MyApp-Work-MyCase";
// prepare a Top-level page
preparePage("MyTopLevelPage").create(myClass)
        .prop("pyLabel", "This is my top level page for a case")
    	.prop("MyProp", "My value");
// prepare parameter
prepareParameter("myParam").value("My param value");

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

// mock nested activity invocation
expect().activity().className("MyOrg-MyApp-Work-MyCase").name("NestedActivity").andMock(new MockBehaviour<MockActivityContext>() {
	@Override
	public void process(MockActivityContext context) throws Exception {
    	       // assert parameters
    	      context.assertParameter("myParam").value("My value");
               // assert Primary page
    	       context.assertPrimaryPage().prop("MyProp", "My value");
               // assert Top-level page
    	       context.assertPage("TopLevelPage").exists().prop("pyLabel", "This is my TLP");
               // set properties in Primary
    	       context.preparePrimaryPage().prop("pyNote", "My note for this page");
    	       // set parameters on Top-level page
    	       context.preparePage("TopLevelPage").prop("pyLabel", "This is MODIFIED TLP");
    	       // set parameters
    	       context.prepareParameter("myResult").value("Success");
	}
});
//mock Obj-Browse method invocation
final String opClass = "Data-Admin-Operator-ID";
expect().objBrowse().page("OperatorList").className(opClass).andMock(new MockBehaviour<MockObjBrowseContext>() {
	@Override
	public void process(MockObjBrowseContext context) throws Exception {
    	      // prepare result set
    	      final PreparePageList pxResults = context.preparePage("OperatorList").prop("pxResultCount", "2").pageList("pxResults");
    	      pxResults.append(opClass).prop("pyUserIdentifier", "info@pegadevops.com").prop("pyLabel", "Operator ID record");
    	      pxResults.append(opClass).prop("pyUserIdentifier", "alexander.lutay@pegadevops.com").prop("pyLabel", "Operator ID record");
	}
});

В тестовом сценарии вы можете вызвать практически любой рул, обладающий поведением в Pega. Модульное тестирование не применимо для долго живущих процессов и GUI, поэтому Ninja не поддерживает Flow, Flow Action, Section и другие связанные с GUI рулы.

// what about covering a Function with a unit test?
invoke().function().ruleSet("MyRuleset").library("MyLibrary").name("Func").args().string("My string").longO(123).date(new Date());
 // would you like to unit test a When rule?
invoke().when().primaryPage("pyWorkPage").name("ToBeOrNotToBe");

Вы можете всесторонне проверять результаты выполнения теста.

// assert activity status
assertActivityStatus().good();
// assert Clipboard state
assertPage("MyTestPage").exists().propAbsent("ErrorCode")
	.prop("ResultCode", "0").propPresent("ResultDescription");
// assert Params
assertParameter("MyParam").value("Some value");

Еще больше примеров можно найти в Ninja Cookbook на GitHub.
С помощью Ninja мы покрыли основные интеграционные сценарии модульными тестами.

  • Мы тестируем Connect activity в изоляции за счет имитации (mocking) других рулов. Это помогло нам сфокусироваться в этих тестах на интеграционной логике и проверить все ветки, и в том числе исключительные ситуации.
  • Трансформации данных были исчерпывающе проверены в изоляции от всех других рулов (Function, различные Decision рулы, источники данных для Data Pages), вызываемых прямо или косвенно из Data Transform рулов.
  • Мы делали заглушки на Connector рулы, чтобы избежать вызова внешних систем из inner-com тестов, а также для проверки XML, формируемого компонентом.

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

Уверенность в качестве приложения

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

Каждая ветка покрыта собственными модульными тестами, которые развиваются вместе с приложением. Тесты хранятся в системе контроля версий и также разделены на ветки. Prod-ветка по состоянию на февраль 2017 года имела 350 тестов. Позволю себе уточнить, что на момент перевода статьи, в июле 2017 года, ветка последнего релиза содержала уже порядка 1650 тестов, время выполнения которых не превышает 15 минут.

Модульные тесты запускаются build-сервером отдельно для каждой ветки каждые 30 минут. Это означает, что команда получает «отчёт о здоровье» системы каждые 30 минут и может своевременно реагировать на появляющиеся дефекты.

Ninja позволила нам быстрее выпускать продукт более высокого качества благодаря меньшему количеству циклов Dev-QA, т.к. большинство дефектов теперь обнаруживаются на Dev-среде и сразу устраняются.

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

Продолжение следует

Наш опыт подсказал нам, что модульные тесты – не единственная «слабая» область платформы Pega: если сравнивать с Java или другими «традиционными» платформами разработки ПО, инструменты Pega, которыми могут воспользоваться разработчики, обладают существенными ограничениями.

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

  • Rule Refactoring – рефакторинг рулов;
  • Code analysis – расширенный статический анализ кода;
  • Code review – аудит кода;
  • Release automation – автоматизация сборок и поставок;
  • Continuous delivery pipeline – конвейер continuous delivery.

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

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

Более подробную информацию о Ninja ищите здесь.

Если вы хотите присоединиться к нашей команде

В заключение предлагаю вам небольшой опрос:

Автор: ГК ЛАНИТ

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js