Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
UI Automation:

Page Objects
Лучше проще, да лучше
Alexei Vinogradov
Alexei Vinogradov

IT-Kонсультант



тестирование, управление тестированием,
автоматизация в тестировании, коучинг
15+ лет...
! !
http://radio-qa.com
Page Objects: идея
Разделить код тестов и 

код нахождения и 

управления элементами страниц
Page Objects: преимущества
Уменьшает дублирование кода
Улучшает читаемость и стабильность
Уменьшает расходы на поддержку и...
UI Автоматизация - это скучно
UI Автоматизация
1. Составить тест-кейсы (не все)
2. Сражаться с тулзами
3. Сражаться с изменениями
4. Повторять 1-3
Лекарство
Пишем свои фреймворки
Justin Searls
Анти-паттерны в Page Objects:
типизированные элементы
Yandex HtmlElements
testIT WebTester
JDI
Button
Link
Image
Checkbox
Типизированные элементы
Это что за элемент?
<a class=„fancy“ href=„javascript:doSmth()“>
Типизированные элементы
A это что за элемент?
<input type=„button“>
Типизированные элементы
A это? :)
<div style=„..some-qooxdoo-magic..“>
Проблемы типизирования
Проверка валидности в Runtime, не Compile-time
Проблемы типизирования
Один элемент - несколько типов
Проблемы типизирования
Нужно задумываться о типе элемента
Усложняет поддержку, если тип „несовершенен“
Вывод
Тотальное типизирование элементов - зло!
Но…
Поддержка стандартных ситуаций - польза:
• Uploads
• Select/Dropdown
• ...
Static Page Objects (Page Modules)
Void Page Objects
Fluent Page Objects
Реализация
http://selenide.org
Selenium inside
UI Testing Framework
open(url)
open(relativeUrl)
Selenide


WebDriver driver=new FirefoxDriver();

driver.get(url);



driver.get(SystemPropert...
$(„cssSelector“)
Selenide
driver.findElement(By.cssSelector("cssSelector"))



или


@FindBy (css="cssSelector")
shouldBe(…), shouldHave(…), should(…)
shouldNotBe(…), etc.
Selenide
Assert + implicit wait +
StaleElementReferenceExceptio...
Приложение
LoginPage HomePage
Static Page Objects
Все элементы и методы - static
Методы возвращают void или сущности
Другое название PageModules*
* Яков...
Static (Page Modules)
public class LoginPage {



public static SelenideElement welcomeMsg=$("#welcome"),

username=$("#us...
Static (Page Modules)
public class LoginPage {



public static SelenideElement welcomeMsg=$("#welcome"),

username=$("#us...
Static (Page Modules)
public class LoginTest {



@Test

public void testLogin(){

open("/");

LoginPage.welcomeMsg.should...
Паттерн public/private (or default)
…

// elements to be used in tests

public static SelenideElement welcomeMsg = $("#wel...
Антипаттерн Getter/Setter
public class LoginPage {



public static SelenideElement welcomeMsg=$("#welcome"),

username=$(...
Антипаттерн Getter/Setter
Ненужные строчки кода
Увеличивает „шум“ в полезном коде
Static imports
import static com.app.pages.LoginPage.*;



public class LoginTest {



@Test

public void testLogin(){

op...
Static imports
import static com.app.pages.FormElement.*;


public class FormTest {



@Test

public void shouldBeInitiall...
Преимущества - недостатки
+ Очень просто и доступно для начинающих
- (очень редко) Проблемы при параллелизации 

(может за...
Приложение
LoginPage HomePage
Void Page Objects
Убираем static из описания полей и методов
Добавляем конструктор
Void Page Objectspublic class LoginPage {



public SelenideElement welcomeMsg = $("#welcome");



SelenideElement usernam...
Void Page Objects
public class LoginTest {



@Test

public void testLogin(){

open("/");

new LoginPage().login("admin","...
Преимущества - недостатки
+ Очень просто и доступно для начинающих
+/? Проверки в конструкторе
+/? можно хранить состояние...
Fluent Page Objects
Методы возвращают PageObject страницы перехода
Если перехода нет - возвращается текущий
PageObject
Fluent Page Objects
public class LoginPage {



public SelenideElement welcomeMsg = $("#welcome");

…

public HomePage log...
Приложение
HomePage
Fluent Page Objects
public class HomePage {



SelenideElement welcomeMsg=$("#welcome"),

reloadBalanceBtn=$("#reloadBalan...
Fluent Page Objects
public class LoginTest {



@Test

public void testLogin(){

open("/");

HomePage homePage=new LoginPa...
Fluent Page Objects
public class FlowTest {



@Test

public void fluentTest(){

new LoginPage().login("admin","12345")

....
Преимущества - недостатки
? переходы между страницами определены в
Page Object - анализ тулзами
-/? некоторые сложности, е...
Приложение
LoginPage
Методы на распутье (Void)
public class LoginTest {



@Test

public void testLogin(){

new LoginPage().login("admin","1234...
Методы на распутье (Fluent)


…



public HomePage login(String username, String pwd) {

doLogin(username, pwd);

return n...
Методы на распутье (Fluent)
public class LoginTest {



@Test

public void testLogin(){

HomePage homePage=new LoginPage()...
Немецкая шутка
Сколько немцев нужно, чтобы вкрутить лампочку?
Анти-паттерны: наследование
Наследование сущностей - О.К.
Анти-паттерны: наследование
Наследование страниц - плохо
Анти-паттерны: наследование
Усложняет поддержку
Лучше пользуйте композицией
Или элементами без композиции
Или копипейстом…*
S.O.L.I.D.
Правила SOLID для тестов - зло!
потому что тестовый код ПОСТОЯННО исполняется
Паттерн Page Elements (Page Blocks)
сложно или невозможно обозначить страницы
блоки элементов повторяются на разных страни...
Тесты с Page Elements без композиции
public class NonCompositeTest {



@Test

public void elementTest(){

new LoginPage()...
Композиция с Page Elements
public class HomePage {



…
public AddressElement addressElement;



public HomePage(){

addre...
Сложности с Page Elements
<div id=„address1“>
…
// code of addressElement
…
</div>
<div id=„address2“>
…
// code of addres...
4 стадии развития
1. Знаю как сделать
2. Знаю как сделать эффективно
3. Знаю как сделать изящно
4. Знаю как не делать
Дублирование кода
SavingAccountPage VisaAccountPage
public class SavingAccountPage {
SelenideElement page=$("#address1"),
...
Дублирование кода
Сopy & Paste безопаснее в Page Object классах,

чем в прочем коде
потому что тестовый код ПОСТОЯННО испо...
Дублирование кода
опасность при рефакторинге
когда код Page Object простой - его редко нужно
рефакторить
Анти-паттерны: чрезмерное
разделение обязанностей
Разделение локаторов и методов в разные файлы
Выделение констант в prope...
Резюме
3 паттерна для написания Page Objects:
Вспомогательные паттерны:
Static
Void
Fluent
public/private доступ
Page Elem...
Резюме
анти-паттерны для Page Objects:
типизированные объекты
наследование
getter/setter
чрезмерное распределение обязанно...
Резюме
темы, не попавшие в доклад
Возвращение методами сущностей
Паттерн DataTransferObjects
Сохранения состояния
Простая ...
Три простые мысли*
В нашем мире не всё, всегда и везде, а кое-что,
иногда и местами. Из любых правил есть
исключения. И пр...
Три простые мысли*
Сложность порождает проблемы, а простые
вещи зачастую самые действенные.
* (с) Дорофеев „Вебинар: Джеда...
Три простые мысли*
Освоен метод или нет становится понятно,
только когда начинаешь его применять. Только
через практику мо...
Напоследок
Всем позитива!
Photo: Sekhar, India ©
The End.
Questions?
skype: alexejv

email: alexei@vinogradov-it.de
twitter: @vinogradoff
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe
Upcoming SlideShare
Loading in …5
×

QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe

1,645 views

Published on

Page Objects, вероятно, самый известный на сегодня паттерн, используемый в автоматизации через UI. И самый простой, скажете вы? Не соглашусь, по моим наблюдением применение данного паттерна таит в себе подводные камни даже для наиболее типичных веб-приложений.

В данном докладе я познакомлю вас с тремя простыми и практичными видами PageObject архитектуры - Static, Void и Fluent, продемонстрирую использование кодом, сравню плюсы и минусы подходов. Также я расскажу о важных недостатках Yandex HTMLElements и о некоторых других неудачных подходах, которые повторяются из одного самодельного фреймворка в другой. Вы увидите на примерах, как простой код решает проблемы лучше, чем слишком умный. Примеры на языке Java/Selenide легко переносимы и на другие языки программирования (.NET, Python и другие).

Published in: Education

QA Fes 2016. Алексей Виноградов. Page Objects: лучше проще, да лучшe

  1. 1. UI Automation:
 Page Objects Лучше проще, да лучше Alexei Vinogradov
  2. 2. Alexei Vinogradov
 IT-Kонсультант
 
 тестирование, управление тестированием, автоматизация в тестировании, коучинг 15+ лет в IT, докладчик SQA Days / Codefest Студент-практикант -> Программист -> Тестировщик ->…
  3. 3. ! !
  4. 4. http://radio-qa.com
  5. 5. Page Objects: идея Разделить код тестов и 
 код нахождения и 
 управления элементами страниц
  6. 6. Page Objects: преимущества Уменьшает дублирование кода Улучшает читаемость и стабильность Уменьшает расходы на поддержку изменений
  7. 7. UI Автоматизация - это скучно
  8. 8. UI Автоматизация 1. Составить тест-кейсы (не все) 2. Сражаться с тулзами 3. Сражаться с изменениями 4. Повторять 1-3
  9. 9. Лекарство Пишем свои фреймворки
  10. 10. Justin Searls
  11. 11. Анти-паттерны в Page Objects: типизированные элементы Yandex HtmlElements testIT WebTester JDI Button Link Image Checkbox
  12. 12. Типизированные элементы Это что за элемент? <a class=„fancy“ href=„javascript:doSmth()“>
  13. 13. Типизированные элементы A это что за элемент? <input type=„button“>
  14. 14. Типизированные элементы A это? :) <div style=„..some-qooxdoo-magic..“>
  15. 15. Проблемы типизирования Проверка валидности в Runtime, не Compile-time
  16. 16. Проблемы типизирования Один элемент - несколько типов
  17. 17. Проблемы типизирования Нужно задумываться о типе элемента Усложняет поддержку, если тип „несовершенен“
  18. 18. Вывод Тотальное типизирование элементов - зло! Но… Поддержка стандартных ситуаций - польза: • Uploads • Select/Dropdown • Tables • …
  19. 19. Static Page Objects (Page Modules) Void Page Objects Fluent Page Objects Реализация
  20. 20. http://selenide.org Selenium inside UI Testing Framework
  21. 21. open(url) open(relativeUrl) Selenide 
 WebDriver driver=new FirefoxDriver();
 driver.get(url);
 
 driver.get(SystemProperties.get(„hostname“)
 + relativeUrl);
  22. 22. $(„cssSelector“) Selenide driver.findElement(By.cssSelector("cssSelector"))
 
 или 
 @FindBy (css="cssSelector")
  23. 23. shouldBe(…), shouldHave(…), should(…) shouldNotBe(…), etc. Selenide Assert + implicit wait + StaleElementReferenceException retry +
 smart logging + screenshot + source code save
  24. 24. Приложение LoginPage HomePage
  25. 25. Static Page Objects Все элементы и методы - static Методы возвращают void или сущности Другое название PageModules* * Яков Крамаренко, „KISS Automation“ (2015)
  26. 26. Static (Page Modules) public class LoginPage {
 
 public static SelenideElement welcomeMsg=$("#welcome"),
 username=$("#username"),
 password=$("#password"),
 loginBtn=$("#login");
 
 public static void login(String user, String pwd){
 username.setValue(user);
 password.setValue(pwd);
 loginBtn.click();
 }
 } 
 public class HomePage { 
 public static SelenideElement welcomeMsg=$("#welcome");
 }
  27. 27. Static (Page Modules) public class LoginPage {
 
 public static SelenideElement welcomeMsg=$("#welcome"),
 username=$("#username"),
 password=$("#password"),
 loginBtn=$("#login");
 
 public static void login(String user, String pwd){
 username.setValue(user);
 password.setValue(pwd);
 loginBtn.click();
 }
 } 
 public class HomePage { 
 public static SelenideElement welcomeMsg=$("#welcome");
 }
  28. 28. Static (Page Modules) public class LoginTest {
 
 @Test
 public void testLogin(){
 open("/");
 LoginPage.welcomeMsg.shouldBe(visible);
 LoginPage.login("admin","12345");
 HomePage.welcomeMsg.shouldBe(visible);
 }
 }

  29. 29. Паттерн public/private (or default) …
 // elements to be used in tests
 public static SelenideElement welcomeMsg = $("#welcome");
 
 // elements to be used in pageObject only
 static SelenideElement username = $("#username"),
 password = $("#password"),
 loginBtn = $("#login");
 …
 
 
 

  30. 30. Антипаттерн Getter/Setter public class LoginPage {
 
 public static SelenideElement welcomeMsg=$("#welcome"),
 username=$("#username"),
 password=$("#password"),
 loginBtn=$("#login");
 
 public static SelenideElement getWelcomeMsg() {
 return welcomeMsg;
 }
 
 public static SelenideElement getUsername() {
 return username;
 }
 
 public static SelenideElement getPassword() {
 return password;
 }
 
 public static SelenideElement getLoginBtn() {
 return loginBtn;
 }
 
 public static void login(String username, String pwd){
 getUsername().setValue(username);
 getPassword().setValue(pwd);
 getLoginBtn().click();
 }
 }

  31. 31. Антипаттерн Getter/Setter Ненужные строчки кода Увеличивает „шум“ в полезном коде
  32. 32. Static imports import static com.app.pages.LoginPage.*;
 
 public class LoginTest {
 
 @Test
 public void testLogin(){
 open("/");
 welcomeMsg.shouldBe(visible);
 login("admin","12345");
 HomePage.welcomeMsg.shouldBe(visible);
 }
 }
 Не всегда хороши:
  33. 33. Static imports import static com.app.pages.FormElement.*; 
 public class FormTest {
 
 @Test
 public void shouldBeInitiallyEmpty(){
 firstname.shouldBe(empty);
 lastname.shouldBe(empty); addressLine1.shouldBe(empty); addressLine2.shouldBe(empty); mobilePhone.shouldBe(empty); }
 }
 Иногда полезны:
  34. 34. Преимущества - недостатки + Очень просто и доступно для начинающих - (очень редко) Проблемы при параллелизации 
 (может зависеть от фреймворка, из-за static) ? меньше гибкости: 
 не рекомендуется хранить состояние 
 нельзя сделать текучий (fluent) интерфейс
  35. 35. Приложение LoginPage HomePage
  36. 36. Void Page Objects Убираем static из описания полей и методов Добавляем конструктор
  37. 37. Void Page Objectspublic class LoginPage {
 
 public SelenideElement welcomeMsg = $("#welcome");
 
 SelenideElement username = $("#username"),
 password = $("#password"),
 loginBtn = $("#login");
 
 public void login(String username, String pwd) {
 …
 }
 
 public LoginPage(){
 welcomeMsg.shouldBe(visible);
 }
 } public class HomePage {
 
 SelenideElement welcomeMsg=$("#welcome");
 
 public SelenideElement loggedInUser=$("#loggedInUser");
 
 public HomePage(){
 welcomeMsg.shouldBe(visible);
 }
 }
  38. 38. Void Page Objects public class LoginTest {
 
 @Test
 public void testLogin(){
 open("/");
 new LoginPage().login("admin","12345");
 HomePage homePage=new HomePage();
 homePage.loggedInUser.shouldHave(text("admin"));
 }
 }
  39. 39. Преимущества - недостатки + Очень просто и доступно для начинающих +/? Проверки в конструкторе +/? можно хранить состояние 
 (пример - выбранная строка таблицы)
  40. 40. Fluent Page Objects Методы возвращают PageObject страницы перехода Если перехода нет - возвращается текущий PageObject
  41. 41. Fluent Page Objects public class LoginPage {
 
 public SelenideElement welcomeMsg = $("#welcome");
 …
 public HomePage login(String username, String pwd) {
 this.username.setValue(username);
 password.setValue(pwd);
 loginBtn.click();
 return new HomePage();
 }
 
 public LoginPage(){
 welcomeMsg.shouldBe(visible);
 }
 }

  42. 42. Приложение HomePage
  43. 43. Fluent Page Objects public class HomePage {
 
 SelenideElement welcomeMsg=$("#welcome"),
 reloadBalanceBtn=$("#reloadBalance"),
 balanceDetailsBtn=$("#balanceDetails");
 
 … }
 
 public HomePage reloadBalance(){
 reloadBalanceBtn.click();
 return this; // return new HomePage();
 }
 
 public BalanceDetailsPage openBalanceDetails(){
 balanceDetailsBtn.click();
 return new BalanceDetailsPage();
 }
 }

  44. 44. Fluent Page Objects public class LoginTest {
 
 @Test
 public void testLogin(){
 open("/");
 HomePage homePage=new LoginPage().login("admin","12345");
 homePage.loggedInUser.shouldHave(text("admin"));
 }
 }
  45. 45. Fluent Page Objects public class FlowTest {
 
 @Test
 public void fluentTest(){
 new LoginPage().login("admin","12345")
 .reloadBalance()
 .openBalanceDetails();
 }
 
 @Test
 public void nonfluentTest(){
 HomePage homePage=new LoginPage().login("admin","12345");
 homePage.reloadBalance();
 BalanceDetailsPage balancePage=homePage.openBalanceDetails();
 }
 }
 Абсолютно неоднозначно, какой из вариантов лучше
  46. 46. Преимущества - недостатки ? переходы между страницами определены в Page Object - анализ тулзами -/? некоторые сложности, если метод возвращает сущности
  47. 47. Приложение LoginPage
  48. 48. Методы на распутье (Void) public class LoginTest {
 
 @Test
 public void testLogin(){
 new LoginPage().login("admin","12345");
 HomePage homePage=new HomePage();
 homePage.loggedInUser.shouldHave(text("admin"));
 }
 
 @Test
 public void testBadLogin(){
 LoginPage loginPage=new LoginPage();
 loginPage.errorMsg.shouldNotBe(visible);
 loginPage.login("admin","admin");
 new LoginPage().errorMsg.shouldBe(visible);
 //loginPage.errorMsg.shouldBe(visible);
 }
 }
  49. 49. Методы на распутье (Fluent) 
 …
 
 public HomePage login(String username, String pwd) {
 doLogin(username, pwd);
 return new HomePage();
 }
 
 public LoginPage badLogin(String username, String pwd) {
 doLogin(username, pwd);
 return new LoginPage(); //return this;
 }
 
 private void doLogin(String username, String pwd) {
 this.username.setValue(username);
 password.setValue(pwd);
 loginBtn.click();
 }
 
 … 
 }
  50. 50. Методы на распутье (Fluent) public class LoginTest {
 
 @Test
 public void testLogin(){
 HomePage homePage=new LoginPage().login("admin","12345");
 homePage.loggedInUser.shouldHave(text("admin"));
 }
 
 @Test
 public void testBadLogin(){
 LoginPage loginPage=new LoginPage().badLogin("admin","admin");
 loginPage.errorMsg.shouldHave(text("Error"));
 }
 }

  51. 51. Немецкая шутка Сколько немцев нужно, чтобы вкрутить лампочку?
  52. 52. Анти-паттерны: наследование Наследование сущностей - О.К.
  53. 53. Анти-паттерны: наследование Наследование страниц - плохо
  54. 54. Анти-паттерны: наследование Усложняет поддержку Лучше пользуйте композицией Или элементами без композиции Или копипейстом…*
  55. 55. S.O.L.I.D. Правила SOLID для тестов - зло! потому что тестовый код ПОСТОЯННО исполняется
  56. 56. Паттерн Page Elements (Page Blocks) сложно или невозможно обозначить страницы блоки элементов повторяются на разных страницах поддержка в Yandex HTMLElements и др. паттерн можно использовать без композиции
  57. 57. Тесты с Page Elements без композиции public class NonCompositeTest {
 
 @Test
 public void elementTest(){
 new LoginPage().login(„admin“,"12345"); HomePage page=new HomePage();
 AddressElement address=new AddressElement();
 page.userName.shouldBe(visible);
 address.zipCode.shouldBe(visible); 
 }
 
 }

  58. 58. Композиция с Page Elements public class HomePage {
 
 … public AddressElement addressElement;
 
 public HomePage(){
 addressElement=new AddressElement(); …
 }
 
 }

  59. 59. Сложности с Page Elements <div id=„address1“> … // code of addressElement … </div> <div id=„address2“> … // code of addressElement … </div> SavingAccountPage VisaAccountPage Посмотрите Yandex HtmlElements Или напишите своё решение Или …
  60. 60. 4 стадии развития 1. Знаю как сделать 2. Знаю как сделать эффективно 3. Знаю как сделать изящно 4. Знаю как не делать
  61. 61. Дублирование кода SavingAccountPage VisaAccountPage public class SavingAccountPage { SelenideElement page=$("#address1"),
 street=page.$(„#street“), city=page.$(„#city“),
 zipCode=page.$(„#zipCode“) …
 } public class VisaAccountPage { SelenideElement page=$("#address2"),
 street=page.$(„#street“), city=page.$(„#city“),
 zipCode=page.$(„#zipCode“) …
 }
  62. 62. Дублирование кода Сopy & Paste безопаснее в Page Object классах,
 чем в прочем коде потому что тестовый код ПОСТОЯННО исполняется
  63. 63. Дублирование кода опасность при рефакторинге когда код Page Object простой - его редко нужно рефакторить
  64. 64. Анти-паттерны: чрезмерное разделение обязанностей Разделение локаторов и методов в разные файлы Выделение констант в properties файлы
  65. 65. Резюме 3 паттерна для написания Page Objects: Вспомогательные паттерны: Static Void Fluent public/private доступ Page Element (Page Blocks) дублирование кода
  66. 66. Резюме анти-паттерны для Page Objects: типизированные объекты наследование getter/setter чрезмерное распределение обязанностей
  67. 67. Резюме темы, не попавшие в доклад Возвращение методами сущностей Паттерн DataTransferObjects Сохранения состояния Простая реализация паттерна композиции
  68. 68. Три простые мысли* В нашем мире не всё, всегда и везде, а кое-что, иногда и местами. Из любых правил есть исключения. И при принятии решений нужно всегда держать голову включенной. * (с) Дорофеев „Вебинар: Джедайская техника доведения дел до конца“
  69. 69. Три простые мысли* Сложность порождает проблемы, а простые вещи зачастую самые действенные. * (с) Дорофеев „Вебинар: Джедайская техника доведения дел до конца“
  70. 70. Три простые мысли* Освоен метод или нет становится понятно, только когда начинаешь его применять. Только через практику можно освоить написание хороших тестов. * (с) Дорофеев „Вебинар: Джедайская техника доведения дел до конца“
  71. 71. Напоследок Всем позитива! Photo: Sekhar, India ©
  72. 72. The End. Questions? skype: alexejv
 email: alexei@vinogradov-it.de twitter: @vinogradoff

×