Selenium RC

Selenium RC (Ruby): Вынесение оконных деклараций в XML-файл

Одной из наиболее серьезных проблем при автоматизации функционального тестирования на уровне GUI является высокая чувствительность тестов к изменениям GUI. По-хорошему, подобная ситуация не должна возникать, так как автоматизация подобного рода ставится тогда, когда пользовательский интерфейс более-менее стабилен. Но это идеальная ситуация. В реальности, продукт меняется по всем направлениям и в том числе это касается пользовательского интерфейса. Так или иначе какие-то мелкие изменения имеют место (поле переименовали, переместили, поменяли некоторые идентификаторы) и это уже влияет на работоспособность тестов. Частично, можно реализовать гибкий механизм поиска объектов, на который подобные изменения не повлияют, но в большинстве случаев корректировок самих реализаций тестов не избежать. Соответственно, надо как-то минимизировать трудозатраты на корректировку. Наиболее эффективным решением данной проблемы можно назвать вынесение оконных деклараций во внешний ресурс и использование "псевдонимов". Подобное реализовано в WinRunner ( GUI Map ), QTP, RFT ( в котором есть возможность маппинга и все оконные объекты можно классифицировать как mappable и non-mappable ), TestComplete ( NameMapping и Alias, появившийся в поздних версиях ). Суть подобных решений в том, чтобы некоторому оконному объекту с заданными атрибутами поставить в соответствие некоторое имя, которое и будет использовано для обращения к данному оконному объекту.

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

Как известно, в Selenium оконные объекты распознаются с помощью локаторов - специальных строк вида:

<how>=<value>, где

how - определяет атрибут, по которому ищется объект. Это может быть id,name, dom, xpath и многие другие ( в документации по Selenium о локаторах достаточно много расписано )
value - непосредственно значение атрибута, по которому ищется объект

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

xpath=//img[@alt=‘The image alt text’]

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

<Псевдоним страницы>.<Псевдоним объекта>

просто для удобства чтения. Для этого можно сделать классы-обертки вида:

class MyPage

     def lnkLink()
          "xpath=//img[@alt='The image alt text']"
     end

end

После чего мы можем создать экземпляр данного класса:

wTestPage = MyPage.new

и вместо локатора использовать выражение вида:

wTestPage.lnkLink

Уже проще, так как в случае модификации атрибутов ссылки нам не надо будет менять локаторы во всех тестах, достаточно будет внести корректировки в объявлении класса. Также это решение удобно тем, что оконные декларации - это такая же часть програмного кода, что и непосредственно реализации тестов. Но тем не менее, немного неудобно нагромождать большое количество подобных классов, а если тестируемое приложение содержит много страниц, то классов будет много, что влечет за собой большое количество файлов. Поэтому, зачастую целесообразно отделить ресурсы ( оконные декларации ) от движка ( непосредственно програмной реализации ). В качестве аналога можно вспомнить NameMapping в TestComplete. Файл, описывающий правила маппинга - это XML-файл. Соответственно, можно попробовать сделать аналогичную реализацию на Ruby. Тем более в данном случае задача заметно проще, так как практически нет иерархии объектов, а сами объекты описываются одной строкой, а не множеством атрибутов.

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

Часть 1

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

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

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

startTimer();
while( getElapsedTime() > iTimeout ){

    i = 0;

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

return -1;

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

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

Selenium RC (Java): Основные операции в действии (Часть 3)

Часть 2

Работа с модальными окнами

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

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

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

Теперь посмотрим, что у нас есть для того, чтобы нормально обрабатывать подобные ситуации.

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

Далее, перед тем как начать работать с элементами внутри появившегося диалога, мы должны перенести фокус на это окно, чтобы Selenium посылал команды конкретному окну. Для этого используется метод selectWindow. В качестве параметра ему надо указать имя данного модального диалога. Обычно оно указывается при вызове openDialog.

Если мы передадим в качестве параметра строку "null", то будет выбрано основное окно. Обычно подобной инструкцией завершается блок кода, работающего внутри модального окна.

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

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

String[] winIDs = selenium.getAllWindows();
selenium.selectWindow( winIDs[ winIDs.length - 1 ] );

Selenium RC (Java): Основные операции в действии. Часть 2

Часть 1, Часть 3

Работа с полем загрузки файлов

Одним из каверзных стандартных элементов управления на веб-страницах с точки зрения работы Selenium-a является поле загрузки файла. Фактически это текстовое поле и кнопка. В чем сложность? Сложность в том, что нажатие на кнопку Selenium-ом напрямую не делается (да и не нужно, если так посмотреть), а текстовое поле в общем случае доступно только для чтения, так что просто так ввести туда путь к файлу не получится. Это связано с тем, что Selenium-сервер взаимодействует с тестируемым веб-приложением путем посылки JavaScript-команд, а у объектов, соответствующих полям ввода файлов, атрибут value закрыт для записи. Это стандартное ограничение браузеров, блокирующее возможность автоматической загрузки произвольных файлов. Не стоит забывать, что клиентские скрипты несут в себе много чего вредоносного. Отчасти поэтому те же файлы с расширением js многими почтовиками блокируются. Это просто система защиты.

Тем не менее, данные проблемы так или иначе можно обойти. Итак, рассмотрим следующую секцию нашей тестовой страницы:

Реализуем шаг, в котором нам надо ввести некоторый текст в поле ввода файла и проверить, что текст введен корректно. Есть 2 способа ввести текст:

  • platform-specific - заключается в использовании сторонних библиотек, которые имитируют ввод с клавиатуры. В частности для Java можно воспользоваться классом java.awt.Robot.
  • browser-specific - заключается в том, что для определенных браузеров в определенном режиме поле ввода файлов доступно для записи на программном уровне. В частности, в режиме *chrome Selenium в состоянии ввести текст в поле ввода файла обычным вызовом type.

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

Итак, нам нужно перейти в *chrome-режим, открыть страницу и ввести текст. Как-то так:

Selenium RC (Java): Основные операции в действии. Часть 1

Часть 2

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

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

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

Для нашего примера нужно создать тестовый HTML файл, в котором будут основные элементы, с которыми мы будем работать. Создадим файл index.htm и поместим его в корневой каталог диска C: . Если вы хотите поместить этот файл в другой каталог, то вам надо будет просто поменять абсолютный путь к файлу и учитывать это вдальнейшем при рассмотрении данного примера.

Итак, поместим в наш HTML-файл содержимое вида:

<html>
<body>
	<form name="sample_form">
		<table>
		<tr><td width="100%"><fieldset><legend>Text fields</legend>
			<table>
				<tr>
					<td><label for="text_field">Simple text field:</td>
					<td><input type=text id="text_field" value="" /></td>
				</tr>
				<tr>
					<td valign=top><label for="text_area">Text Area:</td>
					<td><textarea id="text_area" name="text_area" cols=30 rows=5></textarea></td>
				</tr>
			</table>
		</fieldset></td></tr>
		<tr><td width="100%"><fieldset><legend>Radio buttons/Check boxes</legend>
			<table>
				<tr>
					<td><label for="radio_1">Item 1:</td>
					<td><input type=radio id="radio_1" name="radio_btn" checked /></td>
				</tr>
				<tr>
					<td><label for="radio_2">Item 2:</td>
					<td><input type=radio id="radio_2" name="radio_btn"/></td>
				</tr>
				<tr>
					<td valign=top><label for="check_box">Check box:</td>
					<td><input type=checkbox name="check_box" /></td>
				</tr>
				<tr>
					<td valign=top><label for="ev_check_box">Check box with event:</td>
					<td><input type=checkbox name="ev_check_box" onclick="deactivated_btn.disabled=!this.checked;"/></td>
				</tr>

			</table>
		</fieldset></td></tr>

		<tr><td width="100%"><fieldset><legend>Buttons</legend>
			<table>
				<tr>
					<td><b><div id="clicked_text"></div></b></td>
				</tr>
				<tr>
					<td>
					<input type=button id="deactivated_btn" value="Deactivated" onclick="clicked_text.innerHTML=deactivated_btn.value;"/>
					<input type=button id="btn_1" value="First"  onclick="clicked_text.innerHTML=btn_1.value;"/>
					<button id="btn_2" onclick="clicked_text.innerHTML=btn_2.innerHTML;return false;">Second</button>
					</td>
				</tr>

			</table>
		</fieldset></td></tr>
		<tr><td width="100%"><fieldset><legend>Lists</legend>
			<table>
				<tr>
					<td><label for="single_list">Single selection list:</td>
					<td>
					<select id="single_list" />
						<option selected>Option 1</option>
						<option>Option 2</option>
						<option>Option 3</option>
					</select>
					</td>
				</tr>
				<tr>
					<td valign=top><label for="multi_list">Multi selection list:</td>
					<td>
						<select id="multi_list" MULTIPLE>
						<option>Multi Option 1</option>
						<option>Multi Option 2</option>
						<option>Multi Option 3</option>
						</select>
					</td>
				</tr>
			</table>
		</fieldset></td></tr>
		<tr width="100%"><td width="100%"><fieldset><legend>File input</legend>
			<table>
				<tr>
					<td valign=top><label for="file_input">File upload:</td>
					<td><input type=file name="file_field" /></td>
				</tr>
			</table>

		</fieldset></td></tr>

		<tr><td><fieldset><legend>Dialog boxes</legend>
			<table>
				<tr>
					<td>
						<input type=button id="alert_btn" value="Call Alert"onclick="alert('This is Alert');"/>
						<input type=button id="conf_btn" value="Call Confirmation"  onclick="confirm('This is confirmation');"/>
						<input type=button id="prompt_btn" value="Call Prompt"  onclick="prompt('Enter prompt text')"/></td>
				</tr>
			</table>

		</fieldset></td></tr>

		</table>
	</form>
</body>
</html>

Достаточно просто скопировать данный текст и вставить его в нужный HTML-файл.

Осталось только создать тестовый класс, в котором мы будем добавлять код. Создайте пакет com.example.tests и создайте в нем класс NewTest, который наследуется от SeleneseTestCase класса. После этого каркас теста имеет вид:

package com.example.tests;
// We specify the package of our tests

import com.thoughtworks.selenium.*;
// This is the driver's import. You'll use this for instantiating a
// browser and making it do what you need.

import java.util.regex.Pattern;
// Selenium-IDE add the Pattern module because it's sometimes used for
// regex validations. You can remove the module if it's not used in your
// script.

public class NewTest extends SeleneseTestCase {
// We create our Selenium test case

      public void setUp() throws Exception {
        setUp("c:\\index.htm", "*firefox");
             // We instantiate and start the browser
      }

      public void testNew() throws Exception {
           selenium.open("c:\\index.htm");
     }
}

Этот каркас взят из примеров в документации к селениуму. Просто были сделаны небольшие корректировки. Итак, приступаем.

Selenium RC (Java): Шаги усовершенствования тестов. Часть 2

Часть 1

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

Шаг 4: Абстрагируемся до уровня действий на странице

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

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

selenium.clickAndWait( "leftpanel.newjob" );

а что-то наподобие

mainPage.clickOnNewJobLink();

То есть примитивные операции обернуть в некоторый функционал, который уже отражал бы смысл операции. Это так называемый PageObject-подход, при котором некоторому отдельному окну/странице/форме соответствует некоторый класс, методы которого соответствуют либо каким-то дочерним элементам, либо примитивным действиям внутри данного окна/страницы/формы. Один из примеров подобной реализации можно описан здесь: http://autotestgroup.com/ru/blog/55.html, а точнее реализация подобного для TestComplete. Там было описано, как обернуть некоторые дочерние элементы. В нашем случае применим подход обертки действий над некоторыми элементами, так как Selenium больше ориентирован на действия, которые проводятся над объектом, а не на объекты, над которыми проводятся действия. Это достаточно тонкая грань, которую надо уметь усмотреть.

В любом случае, нам нужен некоторый набор классов, которые могли бы соответствовать некоторым страницам. Сразу следует обратить внимание на то, что если мы скрываем действия Selenium-а внутри некоторого внешнего класса, то нам надо в этот класс передать объект Selenium-а, созданный тестом. Например, при создании любого объекта страницы в качестве параметра передается объект Selenium-a. Пожалуй, это будет наиболее общее для всех объектов страниц решение. В пакет com.mycompany.selenium.lib добавим класс BaseTestClass со следующим содержимым:

/**
 * 
 */
package com.mycompany.selenium.lib;

/**
 * @author KaNoN
 *
 */
public class PageObjectClass {
 
            protected ExtendedSelenium selenium = null;

            public PageObjectClass( ExtendedSelenium selenium ) throws Exception {
                        this.selenium = selenium;
            }
}

После этого, мы можем создавать классы страниц, которые (классы) будут отнаследованы от данного класса. Еще один момент. Когда мы работаем с объектом страницы, то в ряде случаев, когда мы делаем действие, приводящее к переходу на новую страницу, было бы полезно возвращать объект этой новой страницы. Учитывая эти пожелания, создадим отдельный пакет для классов страниц. Назовем его “com.mycompany.selenium.lib.pages” и добавим в него 3 класса страниц, с которыми работает наш тест:

MainPage:

/**
 * 
 */
package com.mycompany.selenium.lib.pages;

import com.mycompany.selenium.lib.ExtendedSelenium;
import com.mycompany.selenium.lib.PageObjectClass;
 

/**
 * @author KaNoN
 *
 */
public class MainPage extends PageObjectClass {

            /**
             * @param selenium
             * @throws Exception
             */

            public MainPage(ExtendedSelenium selenium) throws Exception {
                        super(selenium);
            }

            public NewJobPage clickOnNewJobLink() throws Exception{
                        selenium.clickAndWait( "leftpanel.newjob" );
                        return new NewJobPage( selenium );
            }

            public MainPage clickOnHudsonLink() throws Exception {
                        selenium.clickAndWait("leftpanel.hudson");
                        return this;
            }
}

NewJobPage:

/**
 * 
 */
package com.mycompany.selenium.lib.pages;

import com.mycompany.selenium.lib.ExtendedSelenium;
import com.mycompany.selenium.lib.PageObjectClass;

/**
 * @author KaNoN
 *
 */
public class NewJobPage extends PageObjectClass {

	/**
	 * @param selenium
	 * @throws Exception
	 */
	public NewJobPage(ExtendedSelenium selenium) throws Exception {
		super(selenium);
	}

	public NewJobPage typeJobName( String name ) throws Exception {
		selenium.type( "newjobpage.name", name );
		return this;
	}
	
	public NewJobPage checkFreeStyleJobRadioButton() throws Exception {
		selenium.click("newjobpage.freestyleradio");
		return this;
	}
	
	public ConfigureJobPage clickOK() throws Exception{
		selenium.clickAndWait("newjobpage.ok");
		return new ConfigureJobPage( selenium );
	}

}

ConfigureJobPage:

/**
 * 
 */
package com.mycompany.selenium.lib.pages;

import com.mycompany.selenium.lib.ExtendedSelenium;
import com.mycompany.selenium.lib.PageObjectClass;

/**
 * @author KaNoN
 *
 */
public class ConfigureJobPage extends PageObjectClass {

            /**
             * @param selenium
             * @throws Exception
             */
            public ConfigureJobPage(ExtendedSelenium selenium) throws Exception {
                        super(selenium);
            }

            public MainPage clickSave() throws Exception {
                        selenium.clickAndWait("configurejob.save");
                        return new MainPage( selenium );
            }

}

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

Selenium RC (Java): Шаги усовершенствования тестов. Часть 1

Часть 2

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

И рассмотрим мы эти шаги на примере Selenium-RC (Java). На самом деле, данная тематика не сильно завязана на конкретный инструментарий. Всё то же самое можно сделать практически любым средством, которое в себе содержит возможность программирования тестов. Просто чтобы не уходить в абстрактную теорию, а показать на конкретных примерах, как оно работает, нужно привязаться к какой-то конкретно реализации. Но подобные практики имеют место быть в любых аналогичных решениях. Поэтому, тут можно усматривать не только особенность работы с каким-то конкретным средством, но и стоит обратить внимание на практики в целом, которые будут задействованы.

Итак, путем записи и незначительной корректировки был получен следующий тест:

package com.mycompany.selenium.tests;
import java.util.Date;
import com.thoughtworks.selenium.*;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

/**
 * @author KaNoN
 */

public class SampleTestStage00 {
            private Selenium selenium = null;
            private String genTaskName() {
                        Date dt = new Date();
                        String result = dt.toString();
                        result = result.replaceAll( ":" , "" );
                        result = result.replaceAll( " " , "" );
                        return result;
            }

            @BeforeMethod(alwaysRun=true)
            public void init() throws Exception {
                        selenium = new DefaultSelenium( "localhost" , 4444 , "*iexplore" , "http:// localhost:8080" );
                        selenium.start();
                        selenium.open("http://localhost:8080/hudson");
            }

            @Test(groups = {"sample","sample0"})
            public void testCreateJob() throws Exception {
                        selenium.click("link=New Job");
                        selenium.waitForPageToLoad("30000");
                        selenium.type("name", "SampleTask" + genTaskName() );
                        selenium.click("//input[@value='Build a free-style software project']");
                        selenium.click("//button[@type='button']");
			selenium.waitForPageToLoad("30000");
                        selenium.click("//button[text()='Save']");
                        selenium.waitForPageToLoad("30000");
                        selenium.click("link=Hudson");
                        selenium.waitForPageToLoad("30000");
            }

            @AfterMethod(alwaysRun=true)
            public void stop() throws Exception {
                        selenium.stop();
            }
}

Что он делает? Он запускает веб-приложение (в данном примере это Hudson) и создает в нем новую задачу. Тест реализован с использованием TestNG в качестве тестового движка.

Как видно, тест еще далек от совершенства, как минимум из-за того, что много hard-coded значений. Да и вот так на первый взгляд трудно понять, что этот тест детает вообще. В-общем, непонятных вещей много, нужно усовершенствовать. Вопрос в том, что, когда и (самое главное) как.

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