6 нояб. 2016 г.

Автоматизированное тестирование: обмен данными между клиентом и менеджером


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


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

Данная проблема решилась путем дополнения строки запуска клиента тестирования пользовательским параметром "/C КлиентТестирования".

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

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
 
 ПараметрыЗаписи.Вставить("ЭтоКлиентТестирования", ПараметрЗапуска = "КлиентТестирования");
 
КонецПроцедуры

&НаСервере
Процедура ПриЗаписиНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
 
 Если ПараметрыЗаписи.ЭтоКлиентТестирования Тогда
  СохранитьРезультатТестирования(ТекущийОбъект.Ссылка);
 КонецЕсли;
 
КонецПроцедуры

&НаСервереБезКонтекста
Процедура СохранитьРезультатТестирования(РезультатТестирования)

 ХранилищеОбщихНастроек.Сохранить("ТестовоеПриложение_ДанныеОбмена", "ПоступлениеТоваровУслуг_Запись", РезультатТестирования); 

КонецПроцедуры

Перейдем к реализации сценария с получением данных.



В форме ИнициализацияТеста происходит запуск клиента тестирования и определяется порядок запуска тестов.



&НаКлиенте
Перем КонтекстВыполнения;

#Область ИнициализацияТестовогоПриложения

&НаКлиенте
Процедура ТестДолжен_ИнициализироватьТестовоеПриложение() Экспорт

 КонтекстВыполнения = Новый Структура;
 
 ИмяПользователя = "Админ";
 Пароль = "";
 Порт = 1539;
 ТаймаутОжиданияПриложенияВСекундах = 20;
 
 СтрокаЗапуска = ПолучитьСтрокуЗапуска(ИмяПользователя, Пароль, Порт);
  ЗапуститьПриложение(СтрокаЗапуска);
 
 ТестовоеПриложение = Новый ТестируемоеПриложение(, Порт);
 ВремяОкончанияОжидания = ТекущаяДата() + ТаймаутОжиданияПриложенияВСекундах;
 Подключен = Ложь;
 ОписаниеОшибкиСоединения = "";
 Пока Не ТекущаяДата() >= ВремяОкончанияОжидания Цикл
  Попытка
   ТестовоеПриложение.УстановитьСоединение();
   Подключен = Истина;
   Прервать;
  Исключение
   ОписаниеОшибкиСоединения = ОписаниеОшибки();
  КонецПопытки;
 КонецЦикла;
 Если Не Подключен Тогда
  ТестовоеПриложение = Неопределено;
  ВызватьИсключение "Не смогли установить соединение! " + Символы.ПС + ОписаниеОшибкиСоединения;
 КонецЕсли;
 
 КонтекстВыполнения.Вставить("ТестовоеПриложение", ТестовоеПриложение);
 КонтекстВыполнения.Вставить("ИмяПользователя", ИмяПользователя);
 
КонецПроцедуры

&НаКлиенте
Функция ПолучитьСтрокуЗапуска(ИмяПользователя = "", Пароль = "", Порт = "")

 Результат = КаталогПрограммы() + "1cv8c.exe ENTERPRISE " +
  "/IBConnectionString""" + СтрЗаменить(СтрокаСоединенияИнформационнойБазы(), """", """""") + """" +
  "/N""" + ИмяПользователя + """" + "/P""" + Пароль + """" +  
  " /TESTCLIENT -TPort" + XMLСтрока(Порт) + 
  " /C КлиентТестирования";

 Возврат Результат;
 
КонецФункции

#КонецОбласти

&НаКлиенте
Процедура ТестДолжен_СоздатьПоступлениеТоваровУслуг() Экспорт

 Если КонтекстВыполнения = Неопределено Тогда
  ТестДолжен_ИнициализироватьТестовоеПриложение();
 КонецЕсли;
 
 КонтекстТеста = ПолучитьФорму("ВнешняяОбработка.МодульныйТест.Форма.ТестДолжен_СоздатьПоступлениеТоваровУслуг");
 КонтекстТеста.ВыполнитьТест(КонтекстВыполнения);

КонецПроцедуры

&НаКлиенте
Процедура ВыполнитьТест(Команда)
 
 ТестДолжен_ИнициализироватьТестовоеПриложение();
 ТестДолжен_СоздатьПоступлениеТоваровУслуг();
 
КонецПроцедуры

Форма ТестДолжен_СоздатьПоступлениеТоваровУслуг содержит сам код теста. Код вынесен в отдельную форму по личным соображениям.

&НаКлиенте
Процедура ВыполнитьТест(КонтекстВыполнения) Экспорт

 Перем ТестовоеПриложение;
 
 КонтекстВыполнения.Свойство("ТестовоеПриложение", ТестовоеПриложение);
 Если ТестовоеПриложение = Неопределено Тогда
  ВызватьИсключение "Тестовое приложение не инициализировано.";
 КонецЕсли;
 
 ОкноПриложенияОсновноеКнопкаКомандногоИнтерфейсаПоступлениеТоваровИУслугНажать(ТестовоеПриложение);
 ОкноПриложенияПоступлениеТоваровИУслугКнопкаСоздатьНажать(ТестовоеПриложение);
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеПолеОрганизацияВыбрать(ТестовоеПриложение);
 ОкноПриложенияОрганизацииТаблицаСписокВыбрать(ТестовоеПриложение);
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеПолеКонтрагентВыбрать(ТестовоеПриложение);
 ОкноПриложенияКонтрагентыТаблицаСписокВыбрать(ТестовоеПриложение);
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеПолеОтветственныйВыбрать(ТестовоеПриложение);
 ОкноПриложенияСотрудникиТаблицаСписокВыбрать(ТестовоеПриложение);
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеКнопкаПровестиИЗакрытьНажать(ТестовоеПриложение);
 
 РезультатТеста = ПолучитьЗначениеРезультатаТеста(КонтекстВыполнения.ИмяПользователя);
 Сообщить(РезультатТеста);
 
КонецПроцедуры 

&НаСервереБезКонтекста
Функция ПолучитьЗначениеРезультатаТеста(ИмяПользователя)

 РезультатТеста = 
 ХранилищеОбщихНастроек.Загрузить("ТестовоеПриложение_ДанныеОбмена", "ПоступлениеТоваровУслуг_Запись",, ИмяПользователя);
 ХранилищеОбщихНастроек.Удалить("ТестовоеПриложение_ДанныеОбмена", "ПоступлениеТоваровУслуг_Запись", ИмяПользователя);
 
 Возврат РезультатТеста;

КонецФункции 

#Область СозданиеПоступления
// код не приводится из-за объема, сама обработка в конце статьи
#КонецОбласти

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

 КнопкаПровестиИЗакрыть = 
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеФормаПоступлениеТоваровИУслугСоздание.НайтиОбъект(
 Тип("ТестируемаяКнопкаФормы"), "Провести и закрыть");
 КнопкаПровестиИЗакрыть.Нажать();
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеФормаПоступлениеТоваровИУслугСоздание.ОжидатьЗакрытие();

или выполнить нажатие на кнопку "Провести", а потом выполнить закрытие формы:

 КнопкаПровести = 
 ОкноПриложенияПоступлениеТоваровИУслугСозданиеФормаПоступлениеТоваровИУслугСоздание.НайтиОбъект(
 Тип("ТестируемаяКнопкаФормы"), "Провести");
 КнопкаПровести.Нажать();
 ОкноПриложенияПоступлениеТоваровИУслугСоздание.Закрыть();

Для использования в тесте возможностей фреймворка xUnitFor1C в форму ИнициализацияТеста достаточно добавить код:

&НаКлиенте
Перем КонтекстЯдра;
&НаКлиенте
Перем Ожидаем;
&НаКлиенте
Перем Утверждения;
&НаКлиенте
Перем СтроковыеУтилиты;

&НаКлиенте
Процедура Инициализация(КонтекстЯдраПараметр) Экспорт
 
 КонтекстЯдра = КонтекстЯдраПараметр;
 Утверждения = КонтекстЯдра.Плагин("БазовыеУтверждения");
 Ожидаем = КонтекстЯдра.Плагин("УтвержденияBDD");
 СтроковыеУтилиты = КонтекстЯдра.Плагин("СтроковыеУтилиты");
 
КонецПроцедуры

&НаКлиенте
Процедура ЗаполнитьНаборТестов(НаборТестов) Экспорт
 
 НаборТестов.НачатьГруппу("ТестГруппа", Истина);
 НаборТестов.Добавить("ТестДолжен_ИнициализироватьТестовоеПриложение", , "Инициализация тестового приложения");
 НаборТестов.Добавить("ТестДолжен_СоздатьПоступлениеТоваровУслуг", , "Создание поступления");
 
КонецПроцедуры




Демонстрационная обработка тестирования.

2 комментария:

  1. Привет , Михаил !
    Насколько востребовано тестирование такого типа для разработки ?

    ОтветитьУдалить
    Ответы
    1. Приветствую.
      Полагаю, ровно настолько, насколько разработчик хочет защитить свой код от "внезапных" ошибок.
      Особенно это может проявиться на крупных длительных проектах с коллективной разработкой.
      Вполне вероятна такая ситуация, когда было сделано "незначительное" изменение кода или метаданных,
      данное изменение было проверено разработчиком и протестировано приемщиком,
      но после обновления рабочей базы "неожиданно" отвалился другой особо важный функционал.
      Соответственно, при наличии приемочных сценарных (или любых других) тестов по ключевым операциям
      такой ситуации можно было бы избежать.
      Стоит отметить, что за последний год тема тестирования в сфере 1С активно развивается.
      Сама 1С развивает встроенный функционал автоматизированного тестирования.
      Так же мне известны три разработки с различными подходами к организации процесса:
      xUnitFor1C (https://github.com/xDrivenDevelopment/xUnitFor1C)
      vanessa-behavoir (https://github.com/silverbulleters/vanessa-behavior, https://www.youtube.com/channel/UCXX3ftgZMW0Seq3rxLcguJg)
      tester (https://github.com/grumagargler/tester, http://infostart.ru/public/561157/)

      Удалить