Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
За пределами
              PageObject

               Дмитрий Жарий




atdays.com
Давайте познакомимся!




Дима Жарий
atdays.com      #atdays        2
Аджендиум…                                [OK]
=================================================

1. Вам не нужен PageObject
2. Вам нужен PageObject
3. PageObject – это паттерн, шаблон… ИДЕЯ!
4. Статический PageObject
5. Динамический PageObject
6. За пределами:
   • Паттерн «Цепочка ответственности»
       и .Invoke()
   • Интерфейс IHaveExpectedControls
   • Наследование в тестах


atdays.com                   #atdays                3
ВАМ ДЕЙСТВИТЕЛЬНО НУЖЕН
    PAGE OBJECT?

atdays.com    #atdays         4
Нет. Если у вас парочка
               несложных тестов
public void Wikipedia_Smart_Search_Test()
{
    RemoteWebDriver driver = new InternetExplorerDriver();

     driver.Navigate()
           .GoToUrl("http://en.wikipedia.org/wiki/Main_Page");

     driver.FindElementByCssSelector(@"div#simpleSearch")
           .SendKeys("Webdriver Selenium");

     Assert.AreEqual("http://en.wiki {...} Driver"
                    , driver.Url);
}




atdays.com                   #atdays                             5
Нет. Если у вас несколько
               сложных тестов




atdays.com          #atdays          6
Ведь код можно улучшить




atdays.com      #atdays         7
Как улучшить?
     Вынести часто используемый функционал
     в общедоступные методы
     Отформатировать код
     Добавить комментарии
      driver.FindElement(By.XPath("//a[text()='Log On']"))
      .Click();


      // Click on the Log On link.
      var logOnLink = driver.FindElement(
                             By.XPath("//a[text()='Log On']"));
      logOnLink.Click();

atdays.com                    #atdays                             8
И, если у вас все под
                 контролем…




atdays.com            #atdays        9
ВАМ НЕ НУЖЕН PAGE OBJECT!


atdays.com     #atdays          10
var ddlMonthSelect =
   new
   SelectElement(driver.Fin
                                        Прости, ня! 
   dElementByName(@"EXPIRYD
   ATE_MM"));
               var
   ddlYearSelect = new
   SelectElement(driver.Fin
   dElementByName(@"EXPIRYD
   ATE_YY"));
               var
   txtSecurityCode =
   driver.FindElementByName
   (@"CVV");
               var
   btnContinue =
   driver.FindElementById("
   btnSubmit");


   ddlMonthSelect.SelectByT
   ext("05");

   ddlYearSelect.SelectByTe
   xt("15");

atdays.com                    #atdays               11
Когда приходит Хаос…
     "Тест-простыней" становится слишком
     много

     В коде не разобраться без пол-литру

     На поддержку уходит уж слишком много
     времени

     Легче всё переписать заново

     Начинаете ненавидеть разработчиков,
     которые вынесли div из span

atdays.com             #atdays              12
PageObject – он как книжная
              полка

  Pages

      LoginPage

       → Login(name, passwd)


      MainPage
       → LogOut()
       → GotoProjects()
       → GotoUserProfile()
       → Search(text)
atdays.com                     #atdays   13
PAGE OBJECT – ОН ГИБКИЙ

atdays.com     #atdays        14
PageObject – это:


    Концентрация
   на языке задачи,
а не на языке решения

atdays.com          #atdays      15
PageObject – главная цель

     Обеспечить хранение локаторов в отдельном
     классе

     Обеспечить повторное использование локаторов
     и/или действий над страницей без дублирования
     кода

     Обеспечить слой абстракции от «драйвера» так,
     чтобы в тестах не использовались физические
     элементы идентификации
     элементов управления приложением



atdays.com               #atdays                     16
А выглядит всё вот так



                      MainPage
                                      Т
                                      Е
                                      С
                        Create        Т
                       Account
                         Page
atdays.com            #atdays             17
Демо!




atdays.com    #atdays   18
ЧТО ЭТО БЫЛО? –
    СТАТИЧЕСКИЙ PAGE OBJECT!

atdays.com     #atdays         19
Позитив 01: Тест стал
                       читабелен
public static RemoteWebDriver Driver { get { return WebBrowser.Driver; } }


[TestMethod]
public void Donate_test_static()
{
    MyPages.MainPage.Open();
    MyPages.MainPage.GoToDonatePage();
    MyPages.DonatePage.Donate_50_UAH_Using_Debit_Card();
    MyPages.DonationPaymentsForm.FillDonationForm
        (
            firstName : "Vasya",
            lastName : "Pupkin",
            securityCode : "555"

             );
    // CUT
}

atdays.com                          #atdays                                  20
Позитив 2: Удобная «точка
            входа» в страницы




atdays.com          #atdays          21
Позитив 3: Driver стал доступен
               отовсюду!
public static class WebBrowser
{
    public static RemoteWebDriver _driver = null;

    public static RemoteWebDriver Driver
    {
        get
        {
             _driver = _driver ?? new InternetExplorerDriver();
             return _driver;
        }
    }
}
                  public static RemoteWebDriver Driver {
                         get { return WebBrowser.Driver; }
                  }
atdays.com                    #atdays                             22
И еще раз о позитиве
     Теперь жизнь браузера (драйвера)
     контролирует класс WebBrowser.

     Текущий драйвер доступен из любого
     участка кода

     Любая страница доступна из MyPages.*

     Наш тест теперь помещается на один
     экран монитора

atdays.com            #atdays               23
Ограничения статического
              класса в C#
                                          class   static class
  Наследоваться                           можно   нельзя
  Породить много разных                   можно   нельзя
  Инициализировать параметрами
                                          можно   нельзя
  через конструктор
                                                  полностью нельзя. Очень
  Завалить                                можно   живучие

  Присвоить переменной                    можно   нельзя

  Передать как параметр другому методу    можно   нельзя

  Реализовать интерфейс                   можно   нельзя
  Использовать статические методы         можно   можно

  Использовать статические поля           можно   можно

  Инициализировать при помощи Webdriver           нельзя "из коробки",
                                          можно
  PageFactory                                     нужно написать свою фабрику

atdays.com                            #atdays                                   24
ГИБКОСТЬ – ЭТО ВАЖНО.
    PAGE OBJECT КАК OBJECT

atdays.com     #atdays       25
Тест не изменился! Спасибо,
              MyPages




atdays.com      #atdays           26
Изменения в MyPages
public static class MyPages
{
    public static MainPage MainPage
    {
        get { return new MainPage();}
    }

     public static DonatePage DonatePage
     {
         get { return new DonatePage(); }
     }

}
atdays.com             #atdays              27
Новая структура проекта

     Object               Static




atdays.com      #atdays            28
AbstractPageBase

public abstract class AbstractPageBase
{
    public RemoteWebDriver Driver
    {
        get { return WebBrowser.Driver; }
    }
}




atdays.com           #atdays                29
PaymentResultPage
public class PaymentResultPage : AbstractPageBase
{
    public void WaitUntilExists()
    {
        WebDriverWait wait = new WebDriverWait(Driver,
                                TimeSpan.FromSeconds(30));
        wait.Until(ExpectedConditions.TitleContains(
                   @"Donate-error - Payments"));
    }

     public string GetResultHeaderText()
     {
         var lblFirstHeader = Driver.
                              FindElementById(@"firstHeading");
         return lblFirstHeader.Text;
     }
}
atdays.com                   #atdays                              30
Changelog
     Появился базовый класс
     AbstractPageBase
     Из MyPages.* можно по-прежнему
     получить любую страницу
     Декларация страниц была вынесена из
     MyPages в отдельные файлы
     Мы лишились ограничений статических
     классов
     ТЕПЕРЬ НАС НЕ ОСТАНОВИТЬ!

atdays.com           #atdays               31
Статический/Динамический –
     разница в перспективах




  Статический
atdays.com
                     Динамический
                #atdays             32
ПАТТЕРНЫ (РЕЦЕПТЫ)
    И ИНТЕРФЕЙСЫ (РОЛИ)

atdays.com     #atdays    33
http://bash.im/quote/420885




xxx: тема письма в рабочей почте
      " RE: FW: FW: RE: RE: FW: RE: СРОЧНО!!!!!!"

yyy: Вот это проблема структурных
    организаций


          ЦЕПОЧКА ОТВЕТСТВЕННОСТИ
          /CHAIN OF RESPONSIBILITY/

  atdays.com                  #atdays           34
Chain of Responsibility

                 ??                 ?


               ???    Цепочка ответственности

                       ????


atdays.com                #atdays               35
Iinvokable

public interface IInvokable
{
    void Invoke();
    bool Exists();
}
                 Invoke – вызвать

                 Invokable – то, что можно вызвать

                 I invokable – Я вызываемый(-оя, -ое)!

atdays.com      #atdays                                  36
НужнаяPage.Invoke():
НужнаяPage.Invoke():

Если я уже Exists() – то вот она я!

Если я не Exists(), то я сделаю:
  ПредыдущаяPage.Invoke()
  Потом что-то нажму – вот она я!


                                 Donation
              Donate
                                 Payments
               Page
                                   Form
atdays.com                  #atdays         37
Что дает нам Iinvokable?
     Любую страницу можно вызвать с
     параметрами по умолчанию


     var page = MyPages.
                PaymentResultErrorInvalidCreditCard;

     page.Invoke();




atdays.com                 #atdays                     38
Что дает нам Iinvokable?
     Передать страницу как параметр метода
public void TestThatPageExists(IInvokable page)
{
    page.Invoke();
    Assert.IsTrue(page.Exists());
}

[TestMethod]
public void Test_PaymentResultErrorInvalidCreditCard()
{

    TestThatPageExists(
                  MyPages.PaymentResultErrnvalidCreditCard
                    );
}
atdays.com                   #atdays                         39
Что дает нам Iinvokable?
     Если нужная страница уже открыта (после
     предыдущего теста ) – она будет использована
     повторно

     public void Invoke()
     {
         if (Exists() == false)
         {
             var mainPage = MyPages.MainPage;
             mainPage.Invoke();
             mainPage.GoToDonatePage();
         }
     }


atdays.com                   #atdays                40
I HAVE EXPECTED CONTROLS

atdays.com      #atdays        41
Сышишь, а есть какиета
                  кантролы?
 public interface IHaveExpectedControls : IInvokable
 {
     List<IWebElement> GetExpectedControls();
 }




               .Invoke()
               .Exists()
               .GetExpectedControls()
atdays.com                #atdays                      42
Get Expected Controls
                  (PageObject)
public class DonatePage : AbstractPageBase, IHaveExpectedControls
{
    [FindsBy(Using=@"input[name='amount'][value='50']",
             How = How.CssSelector)]
     public IWebElement rbtnDonate50;

     [FindsBy(Using=@"input[value='Donate by credit/debit card']",
             How = How.CssSelector)]
     public IWebElement btnMakeDonation;

     public List<IWebElement> GetExpectedControls()
     {
         return new List<IWebElement>()
         {
             rbtnDonate50,
             btnMakeDonation
         };
     }
 atdays.com                           #atdays                        43
Позволяет писать
             универсальные тесты
public virtual IHaveExpectedControls CurrentPage
{
       get { return null; }
}

[TestMethod]
public void TestExpectedControls()
{
    CurrentPage.Invoke();

     var expectedControls = CurrentPage.GetExpectedControls();
     foreach (var expectedControl in expectedControls)
     {
              Assert.IsTrue(expectedControl.Displayed);
     }

}
atdays.com                   #atdays                             44
Авто-тесты для авто-тестов?


                           Легкие тесты, которые:
                           1. Открывают каждую страницу
                           2. Проверяет каждый важный
                              элемент страницы




             Основной тест-набор



atdays.com                #atdays                         45
Та да да дам!




atdays.com        #atdays    46
Спасибы:
     За то, что доклад состоялся:
        Спасибо Вам!

     За Invoke()
             Виктору Линчевскому
             Леониду Артемьеву

     Помощь в подготовке доклада:
             Михаил Поляруш
             Андрей Ребров
atdays.com                 #atdays   47
Я не прощаюсь, я говорю: до
              свидания!




Дима Жарий
atdays.com       #atdays           48

More Related Content

За пределами PageObject

  • 1. За пределами PageObject Дмитрий Жарий atdays.com
  • 3. Аджендиум… [OK] ================================================= 1. Вам не нужен PageObject 2. Вам нужен PageObject 3. PageObject – это паттерн, шаблон… ИДЕЯ! 4. Статический PageObject 5. Динамический PageObject 6. За пределами: • Паттерн «Цепочка ответственности» и .Invoke() • Интерфейс IHaveExpectedControls • Наследование в тестах atdays.com #atdays 3
  • 4. ВАМ ДЕЙСТВИТЕЛЬНО НУЖЕН PAGE OBJECT? atdays.com #atdays 4
  • 5. Нет. Если у вас парочка несложных тестов public void Wikipedia_Smart_Search_Test() { RemoteWebDriver driver = new InternetExplorerDriver(); driver.Navigate() .GoToUrl("http://en.wikipedia.org/wiki/Main_Page"); driver.FindElementByCssSelector(@"div#simpleSearch") .SendKeys("Webdriver Selenium"); Assert.AreEqual("http://en.wiki {...} Driver" , driver.Url); } atdays.com #atdays 5
  • 6. Нет. Если у вас несколько сложных тестов atdays.com #atdays 6
  • 7. Ведь код можно улучшить atdays.com #atdays 7
  • 8. Как улучшить? Вынести часто используемый функционал в общедоступные методы Отформатировать код Добавить комментарии driver.FindElement(By.XPath("//a[text()='Log On']")) .Click(); // Click on the Log On link. var logOnLink = driver.FindElement( By.XPath("//a[text()='Log On']")); logOnLink.Click(); atdays.com #atdays 8
  • 9. И, если у вас все под контролем… atdays.com #atdays 9
  • 10. ВАМ НЕ НУЖЕН PAGE OBJECT! atdays.com #atdays 10
  • 11. var ddlMonthSelect = new SelectElement(driver.Fin Прости, ня!  dElementByName(@"EXPIRYD ATE_MM")); var ddlYearSelect = new SelectElement(driver.Fin dElementByName(@"EXPIRYD ATE_YY")); var txtSecurityCode = driver.FindElementByName (@"CVV"); var btnContinue = driver.FindElementById(" btnSubmit"); ddlMonthSelect.SelectByT ext("05"); ddlYearSelect.SelectByTe xt("15"); atdays.com #atdays 11
  • 12. Когда приходит Хаос… "Тест-простыней" становится слишком много В коде не разобраться без пол-литру На поддержку уходит уж слишком много времени Легче всё переписать заново Начинаете ненавидеть разработчиков, которые вынесли div из span atdays.com #atdays 12
  • 13. PageObject – он как книжная полка Pages LoginPage → Login(name, passwd) MainPage → LogOut() → GotoProjects() → GotoUserProfile() → Search(text) atdays.com #atdays 13
  • 14. PAGE OBJECT – ОН ГИБКИЙ atdays.com #atdays 14
  • 15. PageObject – это: Концентрация на языке задачи, а не на языке решения atdays.com #atdays 15
  • 16. PageObject – главная цель Обеспечить хранение локаторов в отдельном классе Обеспечить повторное использование локаторов и/или действий над страницей без дублирования кода Обеспечить слой абстракции от «драйвера» так, чтобы в тестах не использовались физические элементы идентификации элементов управления приложением atdays.com #atdays 16
  • 17. А выглядит всё вот так MainPage Т Е С Create Т Account Page atdays.com #atdays 17
  • 18. Демо! atdays.com #atdays 18
  • 19. ЧТО ЭТО БЫЛО? – СТАТИЧЕСКИЙ PAGE OBJECT! atdays.com #atdays 19
  • 20. Позитив 01: Тест стал читабелен public static RemoteWebDriver Driver { get { return WebBrowser.Driver; } } [TestMethod] public void Donate_test_static() { MyPages.MainPage.Open(); MyPages.MainPage.GoToDonatePage(); MyPages.DonatePage.Donate_50_UAH_Using_Debit_Card(); MyPages.DonationPaymentsForm.FillDonationForm ( firstName : "Vasya", lastName : "Pupkin", securityCode : "555" ); // CUT } atdays.com #atdays 20
  • 21. Позитив 2: Удобная «точка входа» в страницы atdays.com #atdays 21
  • 22. Позитив 3: Driver стал доступен отовсюду! public static class WebBrowser { public static RemoteWebDriver _driver = null; public static RemoteWebDriver Driver { get { _driver = _driver ?? new InternetExplorerDriver(); return _driver; } } } public static RemoteWebDriver Driver { get { return WebBrowser.Driver; } } atdays.com #atdays 22
  • 23. И еще раз о позитиве Теперь жизнь браузера (драйвера) контролирует класс WebBrowser. Текущий драйвер доступен из любого участка кода Любая страница доступна из MyPages.* Наш тест теперь помещается на один экран монитора atdays.com #atdays 23
  • 24. Ограничения статического класса в C# class static class Наследоваться можно нельзя Породить много разных можно нельзя Инициализировать параметрами можно нельзя через конструктор полностью нельзя. Очень Завалить можно живучие Присвоить переменной можно нельзя Передать как параметр другому методу можно нельзя Реализовать интерфейс можно нельзя Использовать статические методы можно можно Использовать статические поля можно можно Инициализировать при помощи Webdriver нельзя "из коробки", можно PageFactory нужно написать свою фабрику atdays.com #atdays 24
  • 25. ГИБКОСТЬ – ЭТО ВАЖНО. PAGE OBJECT КАК OBJECT atdays.com #atdays 25
  • 26. Тест не изменился! Спасибо, MyPages atdays.com #atdays 26
  • 27. Изменения в MyPages public static class MyPages { public static MainPage MainPage { get { return new MainPage();} } public static DonatePage DonatePage { get { return new DonatePage(); } } } atdays.com #atdays 27
  • 28. Новая структура проекта Object Static atdays.com #atdays 28
  • 29. AbstractPageBase public abstract class AbstractPageBase { public RemoteWebDriver Driver { get { return WebBrowser.Driver; } } } atdays.com #atdays 29
  • 30. PaymentResultPage public class PaymentResultPage : AbstractPageBase { public void WaitUntilExists() { WebDriverWait wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(30)); wait.Until(ExpectedConditions.TitleContains( @"Donate-error - Payments")); } public string GetResultHeaderText() { var lblFirstHeader = Driver. FindElementById(@"firstHeading"); return lblFirstHeader.Text; } } atdays.com #atdays 30
  • 31. Changelog Появился базовый класс AbstractPageBase Из MyPages.* можно по-прежнему получить любую страницу Декларация страниц была вынесена из MyPages в отдельные файлы Мы лишились ограничений статических классов ТЕПЕРЬ НАС НЕ ОСТАНОВИТЬ! atdays.com #atdays 31
  • 32. Статический/Динамический – разница в перспективах Статический atdays.com Динамический #atdays 32
  • 33. ПАТТЕРНЫ (РЕЦЕПТЫ) И ИНТЕРФЕЙСЫ (РОЛИ) atdays.com #atdays 33
  • 34. http://bash.im/quote/420885 xxx: тема письма в рабочей почте " RE: FW: FW: RE: RE: FW: RE: СРОЧНО!!!!!!" yyy: Вот это проблема структурных организаций ЦЕПОЧКА ОТВЕТСТВЕННОСТИ /CHAIN OF RESPONSIBILITY/ atdays.com #atdays 34
  • 35. Chain of Responsibility ?? ? ??? Цепочка ответственности ???? atdays.com #atdays 35
  • 36. Iinvokable public interface IInvokable { void Invoke(); bool Exists(); } Invoke – вызвать Invokable – то, что можно вызвать I invokable – Я вызываемый(-оя, -ое)! atdays.com #atdays 36
  • 37. НужнаяPage.Invoke(): НужнаяPage.Invoke(): Если я уже Exists() – то вот она я! Если я не Exists(), то я сделаю: ПредыдущаяPage.Invoke() Потом что-то нажму – вот она я! Donation Donate Payments Page Form atdays.com #atdays 37
  • 38. Что дает нам Iinvokable? Любую страницу можно вызвать с параметрами по умолчанию var page = MyPages. PaymentResultErrorInvalidCreditCard; page.Invoke(); atdays.com #atdays 38
  • 39. Что дает нам Iinvokable? Передать страницу как параметр метода public void TestThatPageExists(IInvokable page) { page.Invoke(); Assert.IsTrue(page.Exists()); } [TestMethod] public void Test_PaymentResultErrorInvalidCreditCard() { TestThatPageExists( MyPages.PaymentResultErrnvalidCreditCard ); } atdays.com #atdays 39
  • 40. Что дает нам Iinvokable? Если нужная страница уже открыта (после предыдущего теста ) – она будет использована повторно public void Invoke() { if (Exists() == false) { var mainPage = MyPages.MainPage; mainPage.Invoke(); mainPage.GoToDonatePage(); } } atdays.com #atdays 40
  • 41. I HAVE EXPECTED CONTROLS atdays.com #atdays 41
  • 42. Сышишь, а есть какиета кантролы? public interface IHaveExpectedControls : IInvokable { List<IWebElement> GetExpectedControls(); } .Invoke() .Exists() .GetExpectedControls() atdays.com #atdays 42
  • 43. Get Expected Controls (PageObject) public class DonatePage : AbstractPageBase, IHaveExpectedControls { [FindsBy(Using=@"input[name='amount'][value='50']", How = How.CssSelector)] public IWebElement rbtnDonate50; [FindsBy(Using=@"input[value='Donate by credit/debit card']", How = How.CssSelector)] public IWebElement btnMakeDonation; public List<IWebElement> GetExpectedControls() { return new List<IWebElement>() { rbtnDonate50, btnMakeDonation }; } atdays.com #atdays 43
  • 44. Позволяет писать универсальные тесты public virtual IHaveExpectedControls CurrentPage { get { return null; } } [TestMethod] public void TestExpectedControls() { CurrentPage.Invoke(); var expectedControls = CurrentPage.GetExpectedControls(); foreach (var expectedControl in expectedControls) { Assert.IsTrue(expectedControl.Displayed); } } atdays.com #atdays 44
  • 45. Авто-тесты для авто-тестов? Легкие тесты, которые: 1. Открывают каждую страницу 2. Проверяет каждый важный элемент страницы Основной тест-набор atdays.com #atdays 45
  • 46. Та да да дам! atdays.com #atdays 46
  • 47. Спасибы: За то, что доклад состоялся: Спасибо Вам! За Invoke() Виктору Линчевскому Леониду Артемьеву Помощь в подготовке доклада: Михаил Поляруш Андрей Ребров atdays.com #atdays 47
  • 48. Я не прощаюсь, я говорю: до свидания! Дима Жарий atdays.com #atdays 48