Попробуем на небольшом примере показать как, используя PageObjects pattern и Selenium (Java), можно достаточно быстро реализовать фреймоворк для автоматизированного тестирования, и начать писать понятные, легко поддерживаемые, а главное работающие тесты.
Пример:Страница LoginPage состоит из двух полей ввода User и Password, кнопки Login. При отправке корректного логина и пароля открывается страница Home на которой есть имя пользователя и ссылка Logout. При отправке НЕкорректного логина и пароля открывается страница ErrorLogin, на которой есть сообщение об ошибке и ссылка Back To Login Page.
Задание:
Написать тестовые скрипты:
1. Корректный Login -> проверка имени пользователя -> Logout
2. Некорректный Login -> проверка сообщения об ошибке -> Back To Login Page
Решение:
1. Создаем рабочий контекст на базе Selenium
Context.java:public class Context {
public static final String BROWSER_IE = "*iexplore";
public static final String BROWSER_FF = "*firefox";
public static final String BROWSER_CH = "*chrome";
private static Context context;
private static String siteUrl;
private Selenium browser;
private SeleniumServer seleniumServer;
private Context() {
}
public static void initInstance(String browserType, String siteURL) {
context = new Context();
siteUrl = siteURL;
context.setBrowser(new DefaultSelenium("localhost", 4444, browserType, siteURL));
context.start();
}
public static Context getInstance() {
if (context == null) {
throw new IllegalStateException("Context is not initialized");
}
return context;
}
public Selenium getBrowser() {
if (browser != null) {
return browser;
}
throw new IllegalStateException("WebBrowser is not initialized");
}
public String getSiteUrl() {
return siteUrl;
}
public void start() {
try {
seleniumServer = new SeleniumServer();
seleniumServer.start();
} catch (Exception e) {
e.printStackTrace();
}
browser.start();
}
public void close() {
browser.close();
browser.stop();
seleniumServer.stop();
}
public void setBrowser(Selenium browser) {
this.browser = browser;
}
}2. Добавляем в абстрактный суперкласс Page ссылку на рабочий контекст context, а также сервисный метод для получения из него объекта Selenium getBrowser().
Page.java:
public abstract class Page {
private Context context;
private String currentPage;
protected Page(String pageUrl) {
this.currentPage = pageUrl;
setContext(Context.getInstance());
init();
parsePage();
}
private void setContext(Context instance) {
this.context = instance;
}
protected abstract void init();
protected abstract void parsePage();
public String getCurrentPage() {
return context.getSiteUrl() + this.currentPage;
}
protected Selenium getBrowser() {
return context.getBrowser();
}
// ....
// service methods...
// ....
}3. Реализуем классы страниц, необходимые при написании тестов для нашего примера, а именно: LoginPage, HomePage и ErrorLoginPage. И добавляем в сервисные методы реальный ввод данных в поля, нажатие кнопок и клики на ссылки
LoginPage.java:
public class LoginPage extends Page {
public static final String PAGE_URL = "/login.html";
protected LoginPage() {
super(PAGE_URL);
}
public static LoginPage openLoginPage() {
LoginPage loginPage = new LoginPage();
loginPage.getBrowser().open(PAGE_URL);
return loginPage;
}
private void setUserName(String userName) {
// код для заполнения поля Username
getBrowser().type("id=UserName", userName);
}
private void setPassword(String password) {
// код для заполнения поля Password
getBrowser().type("id=Password", password);
}
private void pushLoginButton() {
// код для нажатия на кнопку Login
getBrowser().click("id=LoginButton");
}
protected void parsePage() {
// Разбор элементов страницы
// Заполнение необходимых переменных данными со страницы
}
protected void init() {
// Инициализация страницы, например проверка адреса (URL) страницы:
if(!getBrowser().getLocation().equals(PAGE_URL)) {
throw new IllegalStateException("Invalid page is opened");
}
// Можно также добавить проверки наличия необходимых для дальнейшей работы
// объектов и т.д.
}
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();
}
}HomePage.java:
public class HomePage extends Page {
public static final String PAGE_URL = "/home.html";
private String loggedinUserName;
protected HomePage() {
super(PAGE_URL);
}
protected void init() {
// Инициализация страницы
}
protected void parsePage() {
// Разбор элементов страницы
this.loggedinUserName = getBrowser().getText("id=userName");
}
public String getLoggedinUserName() {
return loggedinUserName;
}
public LoginPage logout() {
getBrowser().click("id=LogoutLink");
return new LoginPage();
}
}ErrorLoginPage.java:
public class ErrorLoginPage extends Page {
public static final String PAGE_URL = "/loginError.html";
private String errorMessage;
protected ErrorLoginPage() {
super(PAGE_URL);
}
protected void init() {
// Инициализация страницы
}
protected void parsePage() {
// Разбор элементов страницы
this.errorMessage = getBrowser().getText("id=ErrorMessage");
}
public String getErrorMessage() {
return this.errorMessage;
}
public LoginPage backToLoginPage() {
getBrowser().click("id=BackLink");
return new LoginPage();
}
}4. Пишем тесты на базе JUnit:
public class TestLogin extends TestCase {
public void setUp() {
// Инициализация контекста.
Context.initInstance(Context.BROWSER_IE, "http://www.testtesttestlogin.com");
}
public void testLoginLogout() {
String userName = "tester";
String password = "testPass";
LoginPage loginPage = LoginPage.openLoginPage();
HomePage homePage = loginPage.login(userName, password);
assertEquals(userName, homePage.getLoggedinUserName());
homePage.logout();
}
public void testInvalidLogin() {
String userName = "$tester@#";
String password = "********";
String expectedMessage = "Invalid username or password";
LoginPage loginPage = LoginPage.openLoginPage();
ErrorLoginPage errorLoginPage = loginPage.loginInvalid(userName, password);
assertEquals(expectedMessage, errorLoginPage.getErrorMessage());
errorLoginPage.backToLoginPage();
}
protected void tearDown() throws Exception {
// закрытие браузера
Context.getInstance().close();
}
}Скачать исходный код примеров по PageObjectPattern
Вот все и готово - первые объекты страниц, первые тесты. Попробуйте, может и Вам подойдет подобная архитектура. В любом случае всегда все можно обсудить - любые комментарии с вопросами и конструктивной критикой приветствуются. Вопросы “отвечаются” быстро, критика анализируется... :)
Алексей Булат
Да прибудет с Вами автотестинг.
7 комментариев:
Думаю данный фреймворк действительно очень популярен в силу своей очевидности. По крайней мере на зная таких умных слов как "PageObject Pattern" мы у себя сделали примерно тоже самое только для WinForms приложения (FormsObject pattern или как-то так видимо должен называться :) ). Видимо во времена ООП такое решение первым приходит на ум.
Даже учитывая, что подход действительно популярен, информации и примеров реализации очень мало. Поэтому я и решил поделиться со всеми тем, что имею...
Для кого-то это будет уже пройденный материал, а для кого-то, надеюсь, окажется достаточно полезной информацией, позволяющей сделать шаг вперед.
На счет названия "PageObject Pattern", придумал его не я, хотя думаю именно так я бы его и назвал :)
Поэтому я и решил поделиться со всеми тем, что имею...
И это правильно :)
Сам подумываю на примере нашей системы АТ написать доклад к SQA Days про построение и использование похожего фрэймворка для Windows приложений... (а то такое ощущение что все вокруг Вебом заняты:) )
Вдруг рецензентам понравиться, не зря ведь "Приоритет отдается докладам, которые несут практический опыт решения проблем" :)
Алексей - а вы случаем не сталкивались с рекордерами для селениума под експлорер?
К сожалению нет, да и как-то не люблю я рекордеры... :)
Попробовал Ваш пример ввести в Eclipse.
IDE ругается на
public static LoginPage openLoginPage() {
getBrowser().open(PAGE_URL);
return new LoginPage();
}
из класса LoginPage, говорит "Cannot make a static reference to the non-static method getBrowser() from the type Page"
Вы не подскажете как решить проблему?
Vovs, большое спасибо за выявленную опытным путем багу!
метод должен выглядеть так:
public static LoginPage openLoginPage() {
LoginPage loginPage = new LoginPage();
loginPage.getBrowser().open(PAGE_URL);
return loginPage;
}
В основном посте так же было исправлено.
Еще раз спасибо!
Отправить комментарий