среда, 14 июля 2010 г.

PageObjects pattern или работаем по науке

Хочется поговорить об автоматизированных тестах, а точнее даже об организации фреймворков для них, а если быть более точным об одном из самых распространенных подходов в автоматизации тестирования через GUI - о PageObjects pattern.

Мое первое знакомство с ним состоялось 5 лет назад, когда ничего не подозревая, мой коллега предложил, организовать наш тестовый фреймворк таким образом, чтобы каждой странице соответствовал один класс, тогда мы называли его парсер (Page Parser). Идея прошла свое крещение и была воплощена, а самое главное, живет и до сих пор.

В двух словах про реализацию.


Есть абстрактный супер-класс Page, реализующий основные методы одинаковые для всех страниц тестируемого приложения (например logout), а также абстрактные методы инициализации и сбора необходимых данных со страницы, вызывающиеся из конструктора.

public abstract class Page {
protected Page() {
   init();
   parsePage();
}
protected abstract void init();
protected abstract void parsePage();

// ....
// service methods...
// ....

}

Далее, все классы реальных страниц наследуют супер-класс Page, реализует абстрактные и добавляет свои методы для функций выполняющихся на этой конкретной странице, причем методы взаимодействия с элементами управления, которые не содержат логики описываем как private void, методы выполняющие действия пользователя, вызывающие открытие новой или обновление активной страницы - public с возвращением ожидаемого объекта (public HomePage).

В итоге, при создании объекта страницы сначала она будет проинициализирована, а потом разобрана (parsed), что даст нам гарантию того, что открыта правильная страница и все необходимые данные собраны, т.е. объект готов к использованию.

Пример:
Страница Login Portal состоит из двух полей ввода User и Password, кнопки Login.

Как будет выглядеть наш PageObject?

public class LoginPortalPage extends Page {

protected LoginPortalPage() {
   super();
}

private void setUserName(String userName) {
  // код для заполнения поля Username
}

private void setPassword(String password) {
  // код для заполнения поля Password
}

private void pushLoginButton() {
  // код для нажатия на кнопку Login
}

protected void parsePage() {
  // Разбор элементов страницы
  // Заполнение необходимых переменных данными со страницы
}

protected void init() {
  // Инициализация страницы
  // Проверка корректности загрузки
}
}

Из данного примера видно, что методы в данном классе LoginPortalPage соответствуют тем действиям, которые может делать пользователь на этой странице, а именно ввести имя (setUserName), пароль (setPassword), нажать кнопку Login (pushLogin).

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

private void loginAs(String userName, String password) {
    setUserName(userName);
    setPassword(password);
    pushLoginButton();
}

public HomePage login(String userName, String password) {
    loginAs(userName, password);
    return new HomePage();
}

public ErrorLoginPage loginInvalid(String userName, String password) {
    loginAs(userName, password);
    return new ErrorLoginPage();
}


Таким образом в своем “окончательном”  варианте класс LoginPortalPage будет выглядеть следующим образом:

public class LoginPortalPage extends Page {

protected LoginPortalPage() {
   super();
}

private void setUserName(String userName) {
  // код для заполнения поля Username
}

private void setPassword(String password) {
  // код для заполнения поля Password
}

private void pushLoginButton() {
  // код для нажатия на кнопку Login
}

protected void parsePage() {
  // Разбор элементов страницы
  // Заполнение необходимых переменных данными со страницы
}

protected void init() {
  // Инициализация страницы
  // Проверка корректности загрузки
}

private void loginAs(String userName, String password) {
    setUserName(userName);
    setPassword(password);
    pushLoginButton();
}

public HomePage login(String userName, String password) {
    loginAs(userName, password);
    return new HomePage();
}

public ErrorLoginPage loginInvalid(String userName, String password) {
    loginAs(userName, password);
    return new ErrorLoginPage();
}
}
 

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

Отправить комментарий

Условия копирования публикаций:

Все публикации в данном блоге являются частной собственностью авторов. Любое копирование информации допускается только при условии указания имени автора и активной ссылки на источник.