4.4. Практика написания скриптов

Назад Содержание Дальше

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

 

Во-первых, веб-приложение – это приложение имеющее клиентскую и серверную часть ( как правило ), причем сервер зачастую находится даже не в соседней комнате, а, допустим, в соседней стране или на соседнем континенте. То есть обработка запроса будет идти достаточно долго. Как результат – каждое уважающее себя веб-приложение ТОРМОЗИТ, соответственно, нужно как-то синхронизировать действия скрипта и работы приложения. Как минимум нужно предусмотреть то, что окна приложения могут долго открываться, а также долго закрываться. Для этих целей разработаем функцию, которая ждет появления/исчезновения некоторого окна. В файле Common.inc допишем функцию вида:

Code

[ ] //*************************************************************************************************
[+] //* Function: BOOLEAN SynchWindow( WINDOW wWin , BOOLEAN bAppear NULL optional)
	[ ] //* Description: This function waits untill window given as first parameter appears or
	[ ] //*              disappears depending on second parameter
	[ ] //* Arguments: WINDOW wWin - window to wait
	[ ] //*            BOOLEAN bAppear - (optional) flag which defines whether window should appear/disappear
	[ ] //*                              TRUE by default
	[ ] //* Return values: TRUE - window is in expected state
	[ ] //*************************************************************************************************
[+] BOOLEAN SynchWindow( WINDOW wWin , BOOLEAN bAppear NULL optional)
	[ ] INTEGER iTimeout = Agent.GetOption (OPT_APPREADY_TIMEOUT)
	[ ] HTIMER hTimer
	[ ] 
	[+] if( IsNull( bAppear ) )
		[ ] bAppear = TRUE
	[ ] 
	[ ] hTimer = TimerCreate(hTimer)
	[ ] TimerStart(hTimer)
	[ ] 
	[+] while( TimerValue(hTimer) < iTimeout )
		[+] if( wWin.bExists == bAppear )
			[ ] return TRUE
	[ ] 
	[ ] TimerStop(hTimer)
	[ ] TimerDestroy(hTimer)
	[ ] 
	[ ] return FALSE

 

Здесь есть один маленький нюанс. Время ожидания появления/исчезновения окна определяется исходя из опции Агента Application Ready Timeout. Безусловно, можно завести глобальную переменную, хранящую время ожидания, но в данном примере обойдемся без неё.

 

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

  • Закрыть все окна браузера
  • Запустить браузер
  • Ввести адрес стартовой страницы и дождаться её появления

Реализуется это в appstate, который мы напишем в виде:

Code

[+] appstate apsGoogle() basedon none
	[+] do
		[+] while( Browser.Exists() )
			[ ] Browser.SetActive()
			[ ] Browser.TypeKeys("")
	[+] except
		[ ] ExceptPrint()
	[ ] 
	[+] if( GetTestcaseState() == TCS_ENTERING )
		[ ] Browser.Invoke()
		[ ] SynchWindow( Browser )
		[ ] Browser.Navigate("www.google.com")
		[ ] 
		[+] if( !SynchWindow( wGoogleStart ) )
			[ ] Error("Failed to Navigate the Google")

 

Эту функцию мы поместим в файл Google.inc. Теперь рассмотрим код повнимательнее. Во-первых, браузер закрывается нажатием комбинации клавиш Alt-F4. Причиной этому служит универсальность данного метода. Окна веб-приложения могут не иметь сисемного меню и системных кнопок, что сводит на нет все остальные способы закрытия окна. А данная комбинация клавиш является стандартной. Во-вторых, во время закрытия окон происходит перехват исключений. В принципе, сильно плохо не будет, если мы закроем не все окна. Они будут мешать только тем, что будут забирать на себя определенную память в зависимости от содержимого. Закрытие всех окон браузера – это просто желательная операция, поэтому мы можем двигаться дальше независимо от того, закрылись ли все окна или нет. В-третьих, блок открытия окна приложения запускается только при старте тесткейса. Это вполне рационально, так как стартовать приложение нам нужно только перед запуском тесткейса. Иначе, при запуске более одного тесткейса подряд, приложение будет запускаться на выходе из одного тесткейса и при старте следующего, то есть 2 раза подряд, что занимает время.

 

Следующим моментом выделим использование окна Browser. Вызов Browser.Invoke() осуществляет поиск пути к исполняемому файлу программы-браузера, после чего запускает браузер. Это стандартный метод, который самостоятельно может определить исходя из настроек SilkTest-а какой браузер нужно искать, где его искать и т.д. То есть нам не нужно выдумывать это самим. Весьма показательно использование Browser.Navigate(“www.google.com”). Фактически данные метод вводит в адресной строке определенные текст и жмет Enter. По большому счету, данное окно уже имеет в наличии интерфейс, позволяющий избежать прямого обращения к элементам главного окна ( адресная строка, меню, кнопки перехода). Обращение к основным элементам главного окна завуалировано функциями. Это позволяет не отвлекаться на разные мелочи.

 

А теперь опишем небольшой сценарий ( некоторый минимум ), который мы будем автоматизировать. Сценарий имеет вид:

№ шага Описание Ожидаемые результаты
1 В строке поиска введите ключевое слово TEST и нажмите кнопку Поиск 
  • Откроется первая страница результатов поиска
  • Заголовок окна будет содержать введенное ключевое слово
  • Ссылка на предыдущую страницу отсутствует
  • Введенное ключевое слово содержится либо в названии ссылки либо в текстке под ссылкой
2 Откройте каждую из ссылок в новом окне. Нажмите Alt-F4 , чтобы закрыть окно Выбранная ссылка откроется в новом окне. Окно закроется
3 Нажмите на ссылку Следующая  Откроется 2-я страница, появится ссылка на предыдущую страницу. Введенное ключевое слово содержится либо в названии ссылки либо в текстке под ссылкой
4 Нажмите на ссылку на 3-ю страницу Откроется 3-я страница. Введенное ключевое слово содержится либо в названии ссылки либо в текстке под ссылкой
5 Нажмите на ссылку Предыдущая Откроется 2-я страница
6 Нажмите на ссылку Домашняя страница Google Стартовая страница откроется

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

Code

[+] testcase GoogleLinkNavigation() appstate apsGoogle
	.................................................
	[ ] wGoogleStart.edtSearch.sValue = "TEST"
	[ ] wGoogleStart.btnGoogleSearch.DoClick()
	[+] if( !SynchWindow(wGoogleResults) )
		[ ] Error("No results avaliable")

 

Проверяем заголовок окна. Он должен содержать введенное ключевое слово.

Code

	[+] if( !MatchStr("TEST*","{Browser.sCaption}") )
		[ ] Error("Main window has incorrect caption. Expected: ""TEST*"" . "+
		          "Actual: ""{Browser.sCaption}"" ",FALSE)

 

Проверяем, что открылась первая страница. В классе GooglePageNumbers, соответствующем списку страниц результатов поиска, соответствующее свойство реализовано. Поэтому:

Code

	[+] if( wGoogleResults.tblPages.iCurPage != 1 )
		[ ] Error("It is not first page!!!. Actual page: {wGoogleResults.tblPages.iCurPage} ")

 

Проверяем, что ссылки на предыдущую страницу нет:

Code

		[+] if( wGoogleResults.tblPages.lnkPrevious.Exists() )
			[ ] Error("Link to the first page is avaliable",FALSE)

 

А теперь, достаточно сложная проверка. Нам нужно перебрать все ссылки и все тексты под ними и убедиться, что хотя бы что-то содержит ключевое слово. Ссылки извлечь можно, для этого и создавался класс GoogleNodeList, но функциональности по извлечению объектов текста под ссылкой нет. Её нужно дописать. Эти тексты находятся на том же уровне, что и ссылка на найденную страницу. Причем количество текстовых объектов варьируется. Это связано с тем, что ключевые слова, выделенные жирным шрифтом, воспринимаются как отдельные объекты. Итак, зайдем в файл Google.inc раскроем класс GoogleNodeList и допишем ему метод:

Code

[+] winclass GoogleNodeList : AnyWin
	.............................................................
	[+] LIST OF WINDOW GetTexts(LISTITEM iItem)
		[ ] LIST OF WINDOW lwWins
		[ ] WINDOW wWin
		[ ] INTEGER iValue = 1
		[ ] wWin = this.HtmlColumn("[HtmlTable]{Mask(this.GetLink(iItem).GetFullCaption())}[1]/[HtmlColumn]#1").HtmlText("#{iValue}")
		[+] while( wWin.Exists() )
			[ ] ListAppend(lwWins,wWin)
			[ ] iValue++
			[ ] wWin = this.HtmlColumn("[HtmlTable]{Mask(this.GetLink(iItem).GetFullCaption())}[1]/[HtmlColumn]#1").HtmlText("#{iValue}")
		[ ] return lwWins

 

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

Code

[+] private VOID SearchForKeyWord( STRING sKeyWord )
	[ ] LIST OF WINDOW lwWins
	[ ] LIST OF STRING lsLinks
	[ ] INTEGER iValue
	[ ] STRING sValue
	[ ] 
	[ ] lsLinks = wGoogleResults.Nodes.lsLinkNames
	[ ] iValue = ListCount( lsLinks )
	[ ] 
	[+] while( iValue )
		[ ] lwWins = wGoogleResults.Nodes.GetTexts(iValue)
		[ ] sValue = lwWins[1].GetFullCaption()
		[ ] 
		[+] if( !MatchStr( "*{sKeyWord}*",sValue) && !MatchStr("*{sKeyWord}*",lsLinks[iValue]) )
			[ ] Error("Neither link ""{lsLinks[iValue]}"" nor descriptive text ""{sValue}"" contain ""{sKeyWord}"" text ",FALSE)
		[ ] 
		[ ] iValue--

 

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

 

Используем функцию и допишем данный шаг:

Code

	[ ] SearchForKeyWord( "TEST" )

 

Следующий шаг будет открывать все ссылки в новом окне. Класс GoogleNodeList уже снабжен методом Select, который позволяет выполнить данную операцию. Сложность возникает в том, чтобы проверить, что некоторая страница открылась. Дело в том, что мы не имеем представления о том, что за страница должна открыться вообще. Мы не знаем этих страниц, но как-то проверить это нужно. Но одно известно – при нажатии на ссылке страница будет загружаться. Мы можем дождаться, пока она загрузится, а потом закрыть ее. Если это была страница результатов поиска ( то есть в новом окне ссылка не открылась ), то тесткейс досрочно завершит работу, так как закрыто главное рабочее окно. Более того, после закрытия некоторой страницы нам нужно дождаться, пока страница результатов поиска вновь активируется. В этом шаге нам понадобятся переменные. Во-первых, нам нужно перебрать все ссылки, соответственно должен быть какой-то итератор. Добавим в начало тесткейса объявление INTEGER iValue. Далее, нам нужно ждать, когда ссылка будет открыта в новом окне и потом, когда страница результатов поиска активируется снова. В первом случае можно воспользоваться Browser.WaitForReady – стандартным методом, который ждет в течение некоторого промежутка времени пока страница загрузится. А вот во втором случае нам нужно самим фиксировать время по аналогии с функцией SynchTime. Разница лишь в том, что в данном случае нужно дождаться, пока свойство bActive не станет возвращать значение TRUE. Можно и это реализовать функцией, но в нашем тесткейсе такой код нужен только в этом шаге, поэтому не будем на этом заостряться. Обратим внимание, что нам нужна переменная для таймера HTIMER hTimer. И еще одно. Время ожидания тоже надо как-то задавать. По аналогии с SynchTime создадим переменную INTEGER iTimeout и проинициализируем её некоторым фиксированным значением. Я задам его равным 30. У меня всё достаточно быстро работает, так что 30 секунд более чем достаточно. Итак, в блоке объявления переменных (в самом начале тесткейса) вписываем следующие строки:

Code

	[ ] HTIMER hTimer
	[ ] INTEGER iValue
	[ ] INTEGER iTimeout = 30

 

Всё, теперь можно реализовывать код шага:

Code

		[ ] iValue = wGoogleResults.Nodes.iLinksCount // Извлекаем количество ссылок на странице
		[ ] 
		[+] while( iValue )
			[ ] wGoogleResults.Nodes.Select(iValue,TRUE) // В цикле выбираем по индексу ссылку и открываем ее в новом окне
			[+] do
				[ ] Browser.WaitForReady() // Ждем, пока страница загрузится
			[+] except
				[ ] Error("Page wasn't loaded during specified timeout") // Если страница не загружается в течение промежутка времени,
							// задаваемого опцией Агента OPT_APPREADY_TIMEOUT, то сгенерируется исключение, означающее,
							// что приложение не готово и еще грузится
			[ ] 
			[ ] Browser.TypeKeys("") // Закрываем страницу нажатием Alt-F4
			[ ] 
			[ ] hTimer = TimerCreate()
			[ ] TimerStart(hTimer)
			[ ] 
			[+] while( !wGoogleResults.bActive )  Ждем, пока страница результатов поиска не активизируется вновь
				[+] if( TimerValue(hTimer) > iTimeout )
					[ ] Error( "Search Results window wasn't activated during specified timeout" )
			[ ] 
			[ ] TimerStop(hTimer)
			[ ] TimerDestroy(hTimer)
			[ ] 
			[ ] iValue--

 

Далее идут действия попроще. Нужно переходить по ссылкам. Итак, кликаем на ссылку Следующая и проверяем, что следующая страница открылась. У объекта wGoogleResults.tblPages, соответствующего таблице со ссылками на страницы результатов поиска, есть свойство iCurPage. Для определения того, что переход на следующую страницу осуществился, поместим ожидание нужного значения в цикл с таймером по аналогии, как мы ждали активации страницы результатов поиска в предыдущем шаге. Делается это так:

Code

	[ ] wGoogleResults.tblPages.lnkNext.Click()
	[ ] 
	[ ] hTimer = TimerCreate()
	[ ] TimerStart(hTimer)
	[ ] 
	[+] while( wGoogleResults.tblPages.iCurPage != 2 )
		[+] if( TimerValue(hTimer) > iTimeout )
			[ ] Error("Second page wasn't activated")
	[ ] TimerStop(hTimer)
	[ ] TimerDestroy(hTimer)

 

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

Code

	[+] if( !wGoogleResults.tblPages.lnkPrevious.Exists() )
		[ ] Error("Link to Previous page doesn't exist",FALSE)
	[ ] 
	[ ] SearchForKeyWord( "TEST" )

 

Следующий шаг реализуется аналогично ( только существование предыдущей ссылки проверять уже не надо ). Единственной разницей является то, что на нужную страницу результатов поиска надо перейти нажатием на номер желаемой страницы. А данная операция реализуется методами объекта wGoogleResults.tblPages. Код имеет вид:

Code

	[ ] wGoogleResults.tblPages.Select("3")
	[ ] 
	[ ] hTimer = TimerCreate()
	[ ] TimerStart(hTimer)
	[ ] 
	[+] while( wGoogleResults.tblPages.iCurPage != 3 )
		[+] if( TimerValue(hTimer) > iTimeout )
			[ ] Error("Third page wasn't activated")
	[ ] TimerStop(hTimer)
	[ ] TimerDestroy(hTimer)
	[ ] 
	[ ] SearchForKeyWord( "TEST" )

 

Следующий шаг еще более урезанный. Нужно только нажать на ссылку Предыдущая и проверить, что вторая страница открылась. Ничего больше проверять не надо. Код:

Code

		[ ] wGoogleResults.tblPages.lnkPrevious.Click()
		[ ] 
		[ ] hTimer = TimerCreate()
		[ ] TimerStart(hTimer)
		[ ] 
		[+] while( wGoogleResults.tblPages.iCurPage != 2 )
			[+] if( TimerValue(hTimer) > iTimeout )
				[ ] Error("Second page wasn't activated")
		[ ] TimerStop(hTimer)
		[ ] TimerDestroy(hTimer)

 

Остается последний шаг, который реализуется так:

Code

	[ ] wGoogleResults.lnkGoogleHomePage.Click()
	[ ] 
	[+] if( !SynchWindow(wGoogleStart) )
		[ ] Error("Home Page wasn't activated")

 

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

 

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


Назад Содержание Дальше