Оптимизация
Selenium RC (Java): Шаги усовершенствования тестов. Часть 2
Опубликовано KaNoN в 16.07.2010Часть 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 );
}
}
Если посмотреть внимательно, то можно увидеть, что каждое действие, которое возвращает объект страницы, либо создает новый объект, либо возвращает указатель на себя, если перехода на новую страницу не было.









