Перейти к содержимому

Ivis

Регистрация: 09 ноя 2007
Offline Активность: 23 окт 2017 14:59
-----

Мои сообщения

В теме: C# - NUnit - WebDriver. Внедрение Page Object. Типовой пример

23 августа 2017 - 09:04

 

 

А зачем везде передавать ApplicationManager, если он реализует Singleton?

 

 

Эмм... не совсем понял.
Непосредственно ApplicationManager я передаю в PageManager, а для страниц я уже использую PageManager с драйвером от ApplicationManager. По крайней мере такая была задумка...

Можете привести конкретный пример кода, или что необходимо исправить, если где-то у меня есть "излишек"?
 

 

Не передавайте ApplicationManager как параметр нигде. Вы всегда можете получить к нему доступ через ApplicationManager.GetInstance(). Потому нет смысла его везде передавать почем зря. Иначе у вас получается масло маслянное, PageManager имеет поле ApplicationManager, а ApplicationManager имеет поле PageManager. Зачем PageManager-у это поле.

 

 

PageManager использует драйвер от ApplicationManager (для инициализации фабрики).

 

И, например, в классе AnyPage.cs, я также использую драйвер в методах проверки наличия элемента на странице и ожидания появления элемента.

    public class AnyPage
    {
        private PageManager pageManager;


        public AnyPage(PageManager pageManager)
        {
            this.pageManager = pageManager;
        }




        public bool IsElementPresentAndVisible(By locator)
        {
            var driver = ApplicationManager.GetInstance().Driver;


            try
            {
                return driver.FindElement(locator).Displayed;
            }
            catch (Exception ex)
            {
                if (ex is NoSuchElementException || ex is ElementNotVisibleException || ex is StaleElementReferenceException)
                {
                    return false;
                }
                else if (ex.InnerException != null && ex.InnerException is StaleElementReferenceException)
                {
                    return false;
                }


                throw ex;
            }
        }


        public IWebElement WaitForElement(By locator)
        {
            var driver = ApplicationManager.GetInstance().Driver;
            bool isFound = false;


            for (int i = 0; i < 30; i++)
            {
                if (IsElementPresentAndVisible(locator))
                {
                    isFound = true;
                    break;
                }
                else
                {
                    System.Threading.Thread.Sleep(1000);
                }
            }


            if (isFound)
            {
                return driver.FindElement(locator); ;
            }
            else
            {
                Assert.Fail("ERROR! It's impossible to detect web-element.");
                return null;
            }
        }


    }

Тут, чтобы всё заработало, мне пришлось прописывать несколько раз 

var driver = ApplicationManager.GetInstance().Driver;

Т.е. дублирование кода налицо.

Но если прописать 

protected IWebdriver driver = ApplicationManager.GetInstance().Driver;

в самом классе, то у меня открывается множество экземпляров браузера.

Как можно решить эту проблему без дублирования кода?


В теме: C# - NUnit - WebDriver. Внедрение Page Object. Типовой пример

23 августа 2017 - 08:54

Опять же, в идеале, тест "не знает", что такое локаторы или element present. Тест работает с контактом.
Например:
app.Contacts.IsContactPresent(newContact)

Фабрика такие вещи делать не может. Для этого нужно писать свою реализацию фабрики (есть ли такая - не знаю), которая будет создавать элементы с изменяемыми локаторами.

 

Вот решение, которое я реализовал.

 

Метод получения элемента в классе страницы: 

        public IWebElement ContactInGrid(ContactData contact)
        {
            By locator = By.XPath("//span[text()=' " + contact.Email + " ' and @class='ng-binding']");
            return WaitForElement(locator);
        }

И класс AnyPage.cs, от которого наследуются все страницы:

    public class AnyPage
    {
        private PageManager pageManager;


        public AnyPage(PageManager pageManager)
        {
            this.pageManager = pageManager;
        }




        public bool IsElementPresentAndVisible(By locator)
        {
            var driver = ApplicationManager.GetInstance().Driver;


            try
            {
                return driver.FindElement(locator).Displayed;
            }
            catch (Exception ex)
            {
                if (ex is NoSuchElementException || ex is ElementNotVisibleException || ex is StaleElementReferenceException)
                {
                    return false;
                }
                else if (ex.InnerException != null && ex.InnerException is StaleElementReferenceException)
                {
                    return false;
                }


                throw ex;
            }
        }


        public IWebElement WaitForElement(By locator)
        {
            var driver = ApplicationManager.GetInstance().Driver;
            bool isFound = false;


            for (int i = 0; i < 30; i++)
            {
                if (IsElementPresentAndVisible(locator))
                {
                    isFound = true;
                    break;
                }
                else
                {
                    System.Threading.Thread.Sleep(1000);
                }
            }


            if (isFound)
            {
                return driver.FindElement(locator); ;
            }
            else
            {
                Assert.Fail("ERROR! It's impossible to detect web-element.");
                return null;
            }
        }
}

    

Вот таким вот образом удалось уйти от использования локаторов и драйвера в тестах, и получать элемент из классов страниц.


В теме: C# - NUnit - WebDriver. Внедрение Page Object. Типовой пример

22 августа 2017 - 08:02

каким образом мне работать с элементами, которых ещё нет на странице? Как их инициализировать?

Искать в тот момент, когда они появятся. А если это вопрос про инициализацию из фабрики, так там все равно, что есть на странице, а чего нет. Инициализация не делает никакого поиска, и вообще не обращается к драйверу. Инициализация просто присваивает полям ссылки на объекты, реализующие IWebElement, которые делают новый поиск элемента при каждом обращении.

 

 

В теории это понятно. Но на практике я не понимаю как это сделать - видимо, не хватает знаний.
 

В моём конкретном примере я работаю с элементом, который появляется на странице после определённых действий в тесте.
И только после этих действий я могу составить локатор, по которому нужно искать элемент на странице.
Сейчас код выглядит следующим образом.

 

Тест:

        [Test]
        public void People_ContactEditingManually()
        {
            // prepare
            string timeStamp = app.Contacts.GetTimestamp(DateTime.Now);


            ContactData newContact = new ContactData("email-update-" + timeStamp + "@qa.test", "FirstName-update-" + timeStamp) {
                LastName = "LastName-update-" + timeStamp,
                Phone = "+1 888 340 72 32",
                Title = "Title-update-" + timeStamp,
                Company = "Company-update-" + timeStamp
            };


            // action
            app.Contacts.UpdateContactManually(newContact);


            // verification 
            By locatorOfUpdatedContactInGrid = app.Pages.People.GetLocatorOfContactInGrid(newContact);


            app.Contacts.WaitForElement(locatorOfUpdatedContactInGrid);
            Assert.IsTrue(app.Contacts.IsElementPresent(locatorOfUpdatedContactInGrid));


        }
И метод в классе страницы:
        public By GetLocatorOfContactInGrid(ContactData contact)
        {
            return By.XPath("//span[text()=' " + contact.Email + " ' and @class='ng-binding']");
        }

Можете подсказать как мне конкретно переписать этот код теста и страницы, чтобы получать из класса страницы веб-элемент, а не локатор?
Как мне инициализировать такой веб-элемент при помощи PageFactory?
 


В теме: C# - NUnit - WebDriver. Внедрение Page Object. Типовой пример

21 августа 2017 - 14:01

 

А как это будет тогда выглядеть с точки зрения структуры тестового приложения? ApplicationManager, получается, будет управлять и хелперами, и объектами страниц?

 

Можно для страниц сделать отдельный менеджер, если так удобнее.

Главное, чтобы зависимость была однонаправленная:

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

- драйвер используется только в страницах

 

 

Кстати, а допустимо ли в тестах и хелперах работать с локаторами?
К примеру, я жду появляение определённого веб-элемента на странице, и чтобы его выявить, я
- передаю определённые данные в класс страницы
- получаю локатор от класса страницы
- полученный локатор использую в тесте для ожидания веб-элемента.

Если недопустимо, то каким образом мне работать с элементами, которых ещё нет на странице? Как их инициализировать?

Пробовал вот такую штуку в классе страницы, но в результате на поиске элемента получаю ошибку "System.NullReferenceException : Object reference not set to an instance of an object.".

        public IWebElement ContactInGrid(ContactData contact)
        {
            By locator = By.XPath("//span[text()=' " + contact.Email + " ' and @class='ng-binding']");
            return driver.FindElement(locator);
        }

В теме: Борьба со "StaleElementReferenceException: Element is no longer at

21 августа 2017 - 13:39

В общем, разобрался, вроде.

 

Если переписать метод проверки видимости элемента следующим образом, то всё заработает.

        public bool IsElementPresentAndVisible(IWebElement webElement)
        {
            try
            {
                return webElement.Displayed;
            }
            catch (Exception ex)
            {
                if (ex is NoSuchElementException || ex is ElementNotVisibleException || ex is StaleElementReferenceException)
                {
                    return false;
                }
                else if (ex.InnerException != null && ex.InnerException is StaleElementReferenceException)
                {
                    return false;
                }


                throw ex;
            }
        }

Может кому пригодится...