WebDriver: Основные команды

В данной заметке я хочу привести описание и примеры основных (на мой взгляд) команд в Selenium 2.0 WebDriver для автоматизации функционального тестирования web-приложений. В данной заметке я рассказывал, что собой представляет WebDriver. А в данной заметке можно посмотреть пример простейшего тестового сценария на WebDriver и основную вводную информацию про данный инструмент автоматизированного тестирования. Примеры будут представлены на C# Driver, версия библиотек — 2.9.0.

Доступ к основным функциям WebDriver предоставляет одноименная сборка WebDriver. Интерфейс IWebDriver является базовым классом, его методы можно разделить на три категории: Control of the browser (OpenQA.Selenium.IWebElement) — методы для работы с элементами страницы, Key properties and methods (OpenQA.Selenium.IWebDriver.Url) — методы для навигации, установки параметров и т.д., и другие вспомогательные методы (например OpenQA.Selenium.ISearchContext.FindElement). Непосредственно за работу с элементами страницы отвечает интерфейс IWebElement. Так как в WebDriver каждый браузер имеет свой драйвер (список поддерживаемых драйверов), то для каждого драйвера предполагается наличие собственной реализации WebElement. Например, ChromeWebElement для Chrome, FirefoxWebElement для FF и т.д. То есть для каждого поддерживаемого браузера предусмотрен свой нативный интерфейс. Также в WebDriver есть HtmlUnitDriver – кроссплатформенный виртуальный браузер. Примерчики инициализации:

Объявляем драйвер:

1
2
     IWebDriver driver = new DriverName();
     driver.<user hits space>
     IWebDriver driver = new DriverName();
     driver.<user hits space>

Поиск элемента:

1
     IWebElement element = driver.findElement(By.id("some_id")); 
     IWebElement element = driver.findElement(By.id("some_id")); 

В новой версии Selenium организован улучшенный механизм поиска элементов страницы. Перед обращением к элементу страницу необходимо сперва вызвать метод поиска с указанием типа локатора. Также есть метод для поиска группы элементов findElements (By by). В WebDriver доступна работа со следующими типами локаторов: id, name, xpath, tagName, className, cssSelector, linkText, partionalLinkText. Пример инициализации и поиска элемента с использованием разных типов локаторов:

1
2
3
4
     IWebElement element;
     element = driver.FindElement(By.Id("LoginControl_LoginButton"));
     element = driver.FindElement(By.Name("LoginControl$LoginButton"));
     element = driver.FindElement(By.XPath("//tr/td[3]/input[@id='LoginControl_LoginButton']"));
     IWebElement element;
     element = driver.FindElement(By.Id("LoginControl_LoginButton"));
     element = driver.FindElement(By.Name("LoginControl$LoginButton"));
     element = driver.FindElement(By.XPath("//tr/td[3]/input[@id='LoginControl_LoginButton']"));

Как и в Selenium 1.0, в Selenium WebDriver можно выделить три типа основных команд:

  • Действия — функциональное действие над элементами веб-страницы или браузером. Например, заполнение полей, нажатие на кнопку и другие;
  • Проверки — выполнение проверок на тестируемой странице. Например, проверка того, что определенное поле формы имеет указанное значение, или проверка заголовка окна и т.д.;
  • Ожидания — организация ожидания наступления определенных событий на странице или со страницей (например, ожидание загрузки страницы или ожидание загрузки ajax запросов, появление на странице определенного элемента и т.д.).

Команды навигации. За навигацию отвечает класс Navigate.

  • void GoToUrl(string url) – перейти по указанному адресу. Пример: driver.Navigate().GoToUrl("some_url");
  • void Back() — вернуться на предыдущую страницу. Пример: driver.Navigate().Back();
  • void Forward() — перейти на следующую страницу. Пример: driver.Navigate().Forward();
  • void Refresh() — обновить страницу. Пример: driver.Navigate().Refresh().

Работа с окнами браузера:

  • void Close() — закрыть текущее окно. Закрывает браузер, если нету больше открытых окон;
  • void Dispose() — Member of System.IDisposable. С помощью данного метода можно удалять, освобождать или сбрасывать неуправляемые ресурсы;
  • Manage() — позволяет изменять настройки драйвера;
  • void Quit() выход из драйвера, закрытие всех окон связанных с ним;
  • OpenQA.Selenium.IWebElement FindElement(OpenQA.Selenium.By by) — поиск элемента на странице. Параметр By указывает на механизм поиска элемента. Возвращает первый найденный элемент, удовлетворяющий, условию поиска. Примеры представлены выше;
  • System.Collections.ObjectModel.ReadOnlyCollection FindElements(OpenQA.Selenium.By by) — то же самое, что и FindElement, только возвращает все элементы, удовлетворяющие условию поиска;
  • OpenQA.Selenium.ITargetLocator SwitchTo() — переключение между popup- ми, алертами, окошками;
  • string PageSource { get; } — возвращает содержимое последней загруженной страницы;
  • string Title { get; } — возвращает Title текущей страницы;
  • string Url { set; get; } — устанавливает или возвращает Url текущего окна;
  • string CurrentWindowHandle { get; } — возвращает ссылку на текущее окно;
  • System.Collections.ObjectModel.ReadOnlyCollection WindowHandles { get; } — возвращает ссылки на все открытые окна браузера.

Доступ к данным командам осуществляется через интерфейс IWebDriver. Ниже простые примеры:

Проверяем Title текущей страницы:

1
      Assert.AreEqual("Bugs Catcher", driver.Title); 
      Assert.AreEqual("Bugs Catcher", driver.Title); 

Проверяем, есть ли на текущей странице текст «Результаты поиска»:

1
      Assert.IsTrue(driver.PageSource.Contains("Результаты поиска")); 
      Assert.IsTrue(driver.PageSource.Contains("Результаты поиска")); 

Примеры работы с popup- ми, алертами. Для переключения между окнами, popup- ми, алертами нужно использовать метод SwitchTo(). Для работы с алертами предназначен класс IAlert.

Выбираем фрейм по имени:

1
     driver.SwitchTo().Frame("frameName");
     driver.SwitchTo().Frame("frameName");

Обрабатываем алерт:

1
2
     Alert alert = driver.SwitchTo().Alert(); 
     alert.Accept();
     Alert alert = driver.SwitchTo().Alert(); 
     alert.Accept();

Выбираем окно по имени:

1
     driver.SwitchTo().Window("windowsName");
     driver.SwitchTo().Window("windowsName");

Работа с Cookies. WebDriver предоставляет необходимые методы работы с Cookies. Пример объявления переменной для работы с Cookies и удаление всех Cookies:

1
2
      ICookieJar cookieJar = driver.Manage().Cookies;
      cookieJar.DeleteAllCookies();
      ICookieJar cookieJar = driver.Manage().Cookies;
      cookieJar.DeleteAllCookies();

Действия над элементами страницы:

  • void Click() — одиночное нажатие по элементу;
  • string GetAttribute(string attributeName) — возвращает значения атрибута;
  • string GetCssValue(string propertyName) — возвращает значение свойства CSS элемента;
  • void SendKeys(string text) — ввод текста в текстовые поля. При работе с текстовыми полями можно использовать функциональные клавиши, их работу обеспечивает класс Keys. Например: element.SendKeys("sds" + Keys.Up);
  • void Submit() — отправка формы на сервер;
  • bool Displayed { get; } — возвращает значение, которое указывает на то, является ли элемент невидимым;
  • bool Enabled { get; } — возвращает значение, которое указывает на то, является ли элемент видимым;
  • System.Drawing.Point Location { get; } — возвращает координаты элемента;
  • bool Selected { get; } — возвращает значение, является ли данный элемент (checkboxes, radio buttons) выбранным;
  • string TagName { get; } — возвращает имя тега элемента;
  • string Text { get; } — возвращает innerText элемента (без пробелов);
  • System.Drawing.Size Size { get; } — вовращает размеры элемента;
  • void Clear() — очистка содержимого текстового элемента.

Выпадающие списки. Элементы множественного выбора. В WebDriver есть специальный класс SelectElement, который предназначен для упрощения работы с HTML select элементами. Для того, чтобы воспользоваться всеми прелестями данного класса нужно подключить пространство имен OpenQA.Selenium.Support.UI. Данный класс сожержит следующие методы:

  • public void DeselectAll() — очистить все записи. Использовать только для элементов множественного выбора (multiple selections);
  • public void DeselectByIndex(int index) — деактивировать опцию, используя индекс элемента;
  • public void DeselectByText(string text) — деактивировать опцию, используя видимый текст элемента;
  • public void DeselectByValue(string value) — деактивировать опцию, по значению элемента;
  • public void SelectByIndex(int index) — активировать опцию, используя индекс элемента;
  • public void SelectByText(string text) — активировать опцию, используя видимый текст элемента;
  • public void SelectByValue(string value) — активировать опцию, по значению элемента;
  • public System.Collections.Generic.IList AllSelectedOptions { get; } — возвращает все выбранные опции в пределах текущего элемента;
  • public bool IsMultiple { get; } — возвращает значения, является ли данный элемент множественного выбора;
  • public System.Collections.Generic.IList Options { get; } — возвращает список опций для выбранного элемента;
  • public OpenQA.Selenium.IWebElement SelectedOption { get;} — возвращает значение текущего элемента.

В качестве примера для multiple элемента деактивируем все выбранные значения, затем активируем, используя разные команды. Используя команду submit отправляем данные на сервер.

1
2
3
4
5
6
7
8
9
     using OpenQA.Selenium.Support.UI;
     ...
     IWebElement element = driver.FindElement(By.Id("submit"));
     SelectElement select = new SelectElement(driver.FindElement(By.XPath("//select")));
     select.DeselectAll();
     select.SelectByIndex(1);
     select.SelectByText("TestText");
     select.SelectByValue("Value2");
     element.submit();
     using OpenQA.Selenium.Support.UI;
     ...
     IWebElement element = driver.FindElement(By.Id("submit"));
     SelectElement select = new SelectElement(driver.FindElement(By.XPath("//select")));
     select.DeselectAll();
     select.SelectByIndex(1);
     select.SelectByText("TestText");
     select.SelectByValue("Value2");
     element.submit();

Пример работы с Drag And Drop. Drag And Drop команды на данный момент пока работают только для Windows платформы.

1
2
3
      IWebElement element = driver.FindElement(By.Name("source"));
      IWebElement target = driver.FindElement(By.Name("target"));
      (new Actions(driver)).DragAndDrop(element, target).Perform();
      IWebElement element = driver.FindElement(By.Name("source"));
      IWebElement target = driver.FindElement(By.Name("target"));
      (new Actions(driver)).DragAndDrop(element, target).Perform();

Класс Actions предосталвяет дополнительные механизмы взаимодействия с браузером (MouseClick, MouseClicksAndHold, ContextClick, DoubleClick, DragAndDropToOffset и другие).

Немного про ожидания:

WebDriver направлен в первую очередь на работу со сложным, перегруженными AJAX запросами веб-интерфейсами. Разработчики предлагают два механизма ожидания (Explicit Waits, Implicit Waits). Explicit Waits (эксплицитное ожидание), с использованием класса WebDriverWait, — представляет собой фактически циклическое ожидание события.

1
2
3
4
5
6
     driver.Url = "http://URL"; //or other actions with delays loading
     WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
     IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
      {
           return d.FindElement(By.Id("someDynamicElement"));
      });
     driver.Url = "http://URL"; //or other actions with delays loading
     WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
     IWebElement myDynamicElement = wait.Until<IWebElement>((d) =>
      {
           return d.FindElement(By.Id("someDynamicElement"));
      });

Implicit Waits (имплицитное ожидание) – все команды поиска элементов автоматически становятся ожидающими.

1
2
3
     driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
     driver.Url = "http://URL"; //or other actions with delays loading
     IWebElement myDynamicElement = driver.FindElement(By.Id("someDynamicElement"));
     driver.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(10));
     driver.Url = "http://URL"; //or other actions with delays loading
     IWebElement myDynamicElement = driver.FindElement(By.Id("someDynamicElement"));

Естественно, можно использовать свои методы ожидания. Ниже пример метода WaitForTextPresent(). Избегайте использования Thread.sleep().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
       public void WaitForTextPresent(string text)
        {
            const int waitRetryDelayMs = 100; //шаг итерации (задержка)
            const int timeOut = 500;  //время тайм маута 
            bool first = true; 
            
            for (int milliSecond = 0; ; milliSecond += waitRetryDelayMs)
            {
                if (milliSecond > timeOut * 1000)
                {
                    Trace.WriteLine("Timeout: Text '" + text + "' is not found ", "Document");
                    break; //если время ожидания закончилось (элемент за выделенное время не был найден)
                }
 
                if (driver.PageSource.Contains(text))
                {
                    if (!first) Trace.WriteLine("Text is found: '" + text + "'", "Document");
                    break; //если элемент найден
                }
 
                if (first) Trace.WriteLine("Waiting for text is present: '" + text + "'", "Document");
 
                first = false;
                Thread.Sleep(waitRetryDelayMs);
            }
       public void WaitForTextPresent(string text)
        {
            const int waitRetryDelayMs = 100; //шаг итерации (задержка)
            const int timeOut = 500;  //время тайм маута 
            bool first = true; 
            
            for (int milliSecond = 0; ; milliSecond += waitRetryDelayMs)
            {
                if (milliSecond > timeOut * 1000)
                {
                    Trace.WriteLine("Timeout: Text '" + text + "' is not found ", "Document");
                    break; //если время ожидания закончилось (элемент за выделенное время не был найден)
                }

                if (driver.PageSource.Contains(text))
                {
                    if (!first) Trace.WriteLine("Text is found: '" + text + "'", "Document");
                    break; //если элемент найден
                }

                if (first) Trace.WriteLine("Waiting for text is present: '" + text + "'", "Document");

                first = false;
                Thread.Sleep(waitRetryDelayMs);
            }

Вызов WaitForTextPresent:

1
2
3
4
5
     driver.Navigate().GoToUrl(baseURL+"Login.action?targetUrl=%2FHome.action");
     driver.FindElement(By.Id("username")).SendKeys("username");
     driver.FindElement(By.Id("password")).SendKeys("password");
     driver.FindElement(By.Name("login")).Click();
     WaitForTextPresent("New Note");
     driver.Navigate().GoToUrl(baseURL+"Login.action?targetUrl=%2FHome.action");
     driver.FindElement(By.Id("username")).SendKeys("username");
     driver.FindElement(By.Id("password")).SendKeys("password");
     driver.FindElement(By.Name("login")).Click();
     WaitForTextPresent("New Note");

RemoteWebDriver:

Selenium WebDriver умеет запускать тесты на удаленной машине. Для удаленного запуска нужно использовать класс RemoteWebDriver.
Добавляем его в пространство имен.

1
     using OpenQA.Selenium.Remote;
     using OpenQA.Selenium.Remote;

Вот простой пример использования класс RemoteWebDriver для запуска тестов на удаленной машине:

1
2
3
4
5
6
      DesiredCapabilities capabilities = new DesiredCapabilities();
      FirefoxProfile profile = new FirefoxProfile();
      capabilities.SetCapability(true);
      capabilities.SetCapability(FirefoxDriver.PROFILE, profile);
      
      IWebDriver driver = new RemoteWebDriver(new Uri("http://RemoteServerIP/wd/hub"), DesiredCapabilities.Firefox());
      DesiredCapabilities capabilities = new DesiredCapabilities();
      FirefoxProfile profile = new FirefoxProfile();
      capabilities.SetCapability(true);
      capabilities.SetCapability(FirefoxDriver.PROFILE, profile);
      
      IWebDriver driver = new RemoteWebDriver(new Uri("http://RemoteServerIP/wd/hub"), DesiredCapabilities.Firefox());

Для браузера Firefox предусмотрена работа с профайлам браузера. Для этого нужно ковырять класс FirefoxProfile и FirefoxProfileManager.

В WebDriver есть возможность создания скриншотов:

1
2
3
4
5
6
7
8
9
10
     public void TakeScreenshot(IWebDriver driver, string saveLocation)
     {
        ITakesScreenshot screenshotDriver = driver as ITakesScreenshot;
        Screenshot screenshot = screenshotDriver.GetScreenshot();
        screenshot.SaveAsFile(saveLocation, ImageFormat.Png);
     }
...
     IWebDriver driver = new SomeDriver();
     driver.Navigate().GoToUrl("http://www.google.com");
     TakeScreenshot(driver, @"C:screenshot.png");
     public void TakeScreenshot(IWebDriver driver, string saveLocation)
     {
        ITakesScreenshot screenshotDriver = driver as ITakesScreenshot;
        Screenshot screenshot = screenshotDriver.GetScreenshot();
        screenshot.SaveAsFile(saveLocation, ImageFormat.Png);
     }
...
     IWebDriver driver = new SomeDriver();
     driver.Navigate().GoToUrl("http://www.google.com");
     TakeScreenshot(driver, @"C:screenshot.png");

Пример создания скриншота с использованием RemoteWebDriver (пример взят с официального сайта Selenium):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     // Add this class to your code and use this instead of RemoteWebDriver
     // You will then be able to cast it to ITakesScreenshot and call GetScreenshot
 
     public class ScreenShotRemoteWebDriver : RemoteWebDriver, ITakesScreenshot
     {
       /// <summary>
       /// Gets a <see cref="Screenshot"/> object representing the image of the page on the screen.
       /// </summary>
       /// <returns>A <see cref="Screenshot"/> object containing the image.</returns>
       public Screenshot GetScreenshot()
       {
           // Get the screenshot as base64.
           Response screenshotResponse = this.Execute(DriverCommand.Screenshot, null);
           string base64 = screenshotResponse.Value.ToString();
 
           // ... and convert it.
           return new Screenshot(base64);
       }
}
     // Add this class to your code and use this instead of RemoteWebDriver
     // You will then be able to cast it to ITakesScreenshot and call GetScreenshot

     public class ScreenShotRemoteWebDriver : RemoteWebDriver, ITakesScreenshot
     {
       /// <summary>
       /// Gets a <see cref="Screenshot"/> object representing the image of the page on the screen.
       /// </summary>
       /// <returns>A <see cref="Screenshot"/> object containing the image.</returns>
       public Screenshot GetScreenshot()
       {
           // Get the screenshot as base64.
           Response screenshotResponse = this.Execute(DriverCommand.Screenshot, null);
           string base64 = screenshotResponse.Value.ToString();

           // ... and convert it.
           return new Screenshot(base64);
       }
}

Про режим совместимости с Selenium RC WebDriverBackedSelenium я писал в этой заметке.

Нужно отметить, что в WebDriver реализованна возможность использования шаблонов PageObject и PageFactory. Суть данных шаблонов заключается в том, чтобы простейшие операции выделить в отдельные логические блоки, т.е. абстрагировать до уровня действий на определенной странице. Например, у нас есть страница Login. Первым делом мы создадим отдельный класс Login в котором опишем все элементы страницы и методы их поиска. Далее для данного класса выделим все необходимыме методы (действия на странице) используя уже ранее описанные элементы страницы. После чего реализованные методы можно будет использовать на более высоком уровне бизнес логики. Данный подход позволяет значительно снизить расходы на разработку и особенно на поддержку тестового фреймворка. А также сделает ваши тесты более гибкими и реюзаемыми. Для использования всех прелестей описанных выше шаблонов в реализации WebDriver не забудьте подключить пространство имен OpenQA.Selenium.Support.PageObjects. Самый простейший пример реализации шаблона PageObject:

Описываем элементы страницы Login:

1
2
3
        IWebElement LoginButton = driver.FindElement(By.Id("LoginControl_LoginButton"));
        IWebElement UserNameTextBox = driver.FindElement(By.Id("LoginControl_UserName"));
        IWebElement PasswordTextBox = driver.FindElement(By.Id("LoginControl_Password"));
        IWebElement LoginButton = driver.FindElement(By.Id("LoginControl_LoginButton"));
        IWebElement UserNameTextBox = driver.FindElement(By.Id("LoginControl_UserName"));
        IWebElement PasswordTextBox = driver.FindElement(By.Id("LoginControl_Password"));

Описание метода LoginAsAdministrator:

1
2
3
4
5
6
        public void LoginAsAdministrator()
        {
            UserNameTextBox.SendKeys("Administrator");
            PasswordTextBox.SendKeys("admin");
            LoginButton.Click();
        }
        public void LoginAsAdministrator()
        {
            UserNameTextBox.SendKeys("Administrator");
            PasswordTextBox.SendKeys("admin");
            LoginButton.Click();
        }

Некоторые примеры были позаимствованы из официальной документации проекта. Я постарался привести, как мне кажется, все основные команды в Selenium WebDriver. Приведенные примеры демонстрируют лишь минимальные возможности инструмента, но достаточные для успешного начала работы с ним. Удачи!