Синхронизация в автотестах. Часть 2: ожидание одного из возможных событий

Часть 1

Часто при работе автотестов возникают ситуации, когда приложение может вести себя по-разному и в зависимости от полученной реакции приложения нужно выполнить определенные действия. Наиболее четко это выражено в тестировании GUI, когда после выполнения некоторых операций мы ожидаем один из возможных вариантов реакции системы. Самый простой пример: у нас есть окно Notepad и нам надо реализовать функционал по его закрытию. То есть, нужно нажать на крестик в правом верхнем углу. Но если уже был введен некоторый текст, то вначале появится сообщение о подтверждении сохранения изменений. В этом случае нам дополнительно надо еще обработать диалог закрытия окна. Если подобную задачу реализовывать в виде некоторой вспомогательной функции, то обе ситуации надо обрабатывать.

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

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

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }startTimer(); while( getElapsedTime() > iTimeout ){

i = 0;

foreach control in ( controlList ){
    if( control.property == expected ){
        return i;
    }
}

}

return -1;{/syntaxhighlighter}

В этом примере:

  • startTimer() - инициирует работу таймера
  • getElapsedTime() - возвращает время прошедшее с момента инициации таймера
  • iTimeout - максимальное время ожидания
  • expected - ожидаемое значение некоторого проверяемого свойства
  • controlList - список элементов управления, у которых надо проверить значение property на соответствие значению expected.

В результате возвращается индекс объекта, у которого свойство приняло нужное значение первым или -1, если ни один из элементов списка не принял ожидаемое значение свойства. Это прототип, который в разных системах реализуется по-разному. Рассмотрим на примере реализации задачи ожидания появления одного из списка ожидаемых окон для разных систем. В SilkTest это реализуется так:

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }[+] INTEGER WaitWindow( LIST OF WINDOW lwWins , INTEGER iTimeout )
[ ] HTIMER hTimer
[ ] INTEGER i
[ ]
[ ] hTimer = TimerCreate()
[ ]
[ ] TimerStart( hTimer )
[ ]
[+] while( TimerValue( hTimer ) < iTimeout )
[+] for( i = 1 ; i < ListCount( lwWins ) ; i++ )
[+] if( lwWins[i].bExists )
[ ] TimerStop( hTimer )
[ ] TimerDestroy( hTimer )
[ ] return i
[ ]
[ ] TimerStop( hTimer )
[ ] TimerDestroy( hTimer )
[ ]
[ ] return 0{/syntaxhighlighter}

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

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }[+] LIST OF WINDOW lwWins = {…}
[ ] wSomeWin1
[ ] wSomeWin2
[ ] wSomeWin3
[ ]
[+] switch( WaitWindow( lwWins , 30 ) )
[+] case 0
[ ] LogError( “Time expired. No actions were performed” )
[+] case 1
[ ] // Handle wSomeWin1
[+] case 2
[ ] // Handle wSomeWin2
[+] case 3
[ ] // Handle wSomeWin3{/syntaxhighlighter}

Если взять реализацию аналогичной задачи для Selenium RC ( в данном случае рассмотрим решение на Java ), то подобная синхронизирующая функция имеет вид:

{syntaxhighlighter brush: java;fontsize: 100; first-line: 1; }class Synchronization {
public static int waitWindow( Selenium client, String[] locators , int timeout ){
int i;
long start = System.currentTimeMillis();

    while( ( System.currentTimeMillis() - start ) &lt; (long)(timeout * 1000) ){
        for( i = 0 ; i &lt; locators.length ){
            if( client.isElementPresent( locators[i] ) ){
                return i;
            }
        }
    }

    return -1;
}

}{/syntaxhighlighter}

Применяется данное решение по аналогичному шаблону, что и решение для SilkTest, с использованием оператора switch:

{syntaxhighlighter brush: jscript;fontsize: 100; first-line: 1; }String [] locators = { “//img[@alt=‘The image alt text’]” ,
“dom=document.forms[‘myForm’].myDropdown”,
“name=username”
} ;

switch( waitWindow( client , locators , 30 ) ){
case 0:
throw new Exception( “Time expired. No actions were performed” );
case 1:
// TODO Handle first object
case 2:
// TODO Handle second object
case 3:
// TODO Handle third object
}{/syntaxhighlighter}

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

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

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

Часть 1