AT.info ПОСИДЕЛКИ  vKontakte   facebook группа  
Что тестировщик не умеет, а автоматизатор должен знать?
view counter
Ищешь как решить проблему с Selenium?! Спроси людей. Они все знают!
view counter
TestComplete ( JScript ): Описание оконных деклараций через классы-обертки

Автоматизированное тестирование на уровне GUI содержит в себе много сюрпризов, связанных с работой с оконными объектами. Причем сюрприз заключается в том, что наиболее изящное решение проблемы с поддержкой описаний оконных объектов в актуальном состоянии для каждого отдельно взятого средства может быть свое. Но это наиболее удачное решение. Тем не менее, есть ряд механизмов, имеющих аналоги в различных средствах. Одним из примеров является Mapping оконных деклараций, позволяющий некоторым оконным объектам ставить в соответствие некоторый псевдоним. Подобное решение в TestComplete обладает одним ключевым недостатком: при изменении иерархии оконного объекта приходится переделывать маппинг всех дочерних объектов. Альтернативой этому стало введение с 6-й версии TestComplete такой функциональности как Alias, которая позволяла регулировать иерархию псевдонимов. Но у такого решения есть другой недостаток: низкая скорость. Соответственно, нужны механизмы, позволяющие оперативно реагировать на изменение GUI, при этом корректировки желательно минимизировать. Давайте рассмотрим конкретный пример.

У нас есть некоторое диалоговое окно, к которому в коде можно обратиться так:

Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1);

Здесь PRODUCT_NAME - это некоторая константа, содержащая в себе имя процесса. В вышеуказанном диалоге есть текстовое поле Name , к которому обратиться можно так:

Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1).Window( "TEdit" , "*" , 1 );

а также 2 кнопки: OK, Cancel, у которых описания имеют вид соответственно:

Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1).Window( "TButton", "*", 1 )
Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1).Window( "TButton", "*", 2 )

Как видно из описаний, объекты не очень компактно описаны, более того, достаточно нестабильно, поскольку используются индексы. Часто может возникнуть ситуация, когда имена объектов в разной среде разные. Самый простой пример: системные окна сообщений для разных локализаций операционной системы имеют кнопки с текстом на локальном языке. Если заказчик из англоязычной страны, то проблем не возникнет. Но если же автотесты создаются для продукта, который идет и для неанглоязычных потребителей, то тут надо уменьшить привязку к тексту объектов, что было сделано для объектов выше. Теперь, давайте рассмотрим некоторый типичный тестовый сценарий, использующий эти объекты. Допустим нам надо подождать появления диалога в течение 5 секунд. Если он появился, то вводим текст ( неважно какой ) в текстовое поле Name и жмем кнопку OK. В чистом виде это выглядит так:

if( Sys.Process("Inventory").WaitWindow("TForm", "New Text Style", 1 , 5000 ).Exists ) {
        Log.Error( "No Text Style dialog available" );
}
else {
      Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1).Window( "TEdit" , "*" , 1 ).wText = "Some Text";
      Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1).Window( "TButton", "*", 1 ).Click();
}

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

function NewTextStyleDlg()
{
  this.Get = function ()
  {
    return Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1);
  }  
}

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

var dNewTextStyle = new NewTextStyleDlg();

if( Sys.Process( PRODUCT_NAME ).WaitWindow("TForm", "New Text Style", 1 , 5000 ).Exists ) {
        Log.Error( "No Text Style dialog available" );
}
else {
      dNewTextStyle.Get().Window( "TEdit" , "*" , 1 ).wText = "Some Text";
      dNewTextStyle.Get().Window( "TButton", "*", 1 ).Click();
}

Что дальше? У нас есть инструкция по проверке на существование, которая все еще явно обращается к свойствам диалогового окна. Соответственно, мы можем дополнить класс-обертку методом, проверяющим на существование. Модифицированный класс выглядит так:

function NewTextStyleDlg()
{
  this.Get = function ()
  {
    return Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1);
  }  

  this.Exists = function ( iWT )
  {
    if( iWT == undefined )
    {
      iWT = 5000;
    }
    
    Sys.Refresh();
    return Sys.Process( PRODUCT_NAME ).WaitWindow("TForm", "New Text Style", 1,iWT).Exists; 
  }
}

В результате внесенных изменений, тестовый код может быть переписан в виде:

var dNewTextStyle = new NewTextStyleDlg();

if( dNewTextStyle.Exists() ) {
        Log.Error( "No Text Style dialog available" );
}
else {
      dNewTextStyle.Get().Window( "TEdit" , "*" , 1 ).wText = "Some Text";
      dNewTextStyle.Get().Window( "TButton", "*", 1 ).Click();
}

Осталось только обернуть непосредственно объекты. Есть множество возможных вариантов, но мы остановимся на создании методов, которые возвращают сами объекты. Нам нужны методы, которые открывают доступ к текстовому полю Name, а также к кнопке OK и Cancel. Реализуется это так:

function NewTextStyleDlg()
{
  this.Get = function ()
  {
    return Sys.Process( PRODUCT_NAME ).Window("TForm", "New Text Style", 1);
  }  

  this.Exists = function ( iWT )
  {
    if( iWT == undefined )
    {
      iWT = 5000;
    }
    
    Sys.Refresh();
    return Sys.Process( PRODUCT_NAME ).WaitWindow("TForm", "New Text Style", 1,iWT).Exists; 
  }

  this.edtName = function () {
    return this.Get().Window( "TEdit" , "*" , 1 );
  }

  this.btnOK = function () {
    return this.Get().Window( "TButton", "*", 1 );
  }

  this.btnCancel = function () {
    return this.Get().Window( "TButton", "*", 2 );
  }
}

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

var dNewTextStyle = new NewTextStyleDlg();

if( dNewTextStyle.Exists() ) {
        Log.Error( "No Text Style dialog available" );
}
else {
      dNewTextStyle.edtName().wText = "Some Text";
      dNewTextStyle.btnOK().Click();
}

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

© 2009-2010 Портал для автоматизаторов тестирования ПО
Автор проекта Поляруш Михаил | При использовании материалов ссылка на www.automated-testing.info обязательна.
Все замечания и пожелания присылайте на webmaster@automated-testing.info.
ручной датер, маркировщики с термолентой. . компенсаторы сильфонные универсальные
Яндекс.Метрика