Pull to refresh

Разработка автоматизированных тестов на базе Selenium WebDriver 2.x

Reading time7 min
Views29K
Здравствуйте, уважаемое Хабросообщество! Хочу поделиться своим способом разработки и организации написания автоматизированных тестов на базе Selenium WebDriver 2.x на языке программирования C#.
Сразу скажу, что многие тезисы, принципы и методы разработки придуманы не мной (и, наверное, не моими учителями).
Возможно, этот пост поможет тем, кто только-только начинает заниматься автоматизированным тестированием Web-приложений.


Подготовка


Для разработки автоматизированных тестов с использованием Selenium нам необходимы следующие компоненты:
Среда разработки, в нашем случае Visual Studio;
Программа NUnit для автоматического прогона написанных тестов (скачать можно здесь: www.nunit.org/index.php?p=download);
Браузер Mozilla Firefox с двумя установленными для него плагинами: FireBug и FirePath (в данной статье не используются).

Настройка среды разработки


Для написания автоматизированных тестов необходимо создать новый проект типа Class Library (можно создать и консольное приложение, но не в нашем случае). Желательно дать ему название, которое ассоциируется с названием тестируемого проекта. Также, желательно создать тестовый проект в самом солюшене с тестируемым проектом.
После создания тестового проекта к нему необходимо подключить 3 специальные библиотеки для тестов:
NUnit (nunit.framework) – библиотека для непосредственного прогона автоматизированных тестов (не только для Selenium);
WebDriver + WebDriver.Support – непосредственно библиотеки Selenium’a.
Подключить библиотеки к тестовому проекту можно через NuGet или вручную, скачав последнюю версию с сайта code.google.com/p/selenium/downloads/list.

Построение архитектуры тестового проекта


Базовый класс

До начала написания самих автоматизированных тестов вначале нам необходимо создать вспомогательные классы.
В тестовом проекте добавим новый класс и назовем его SeleniumTestBase.cs (можно и по-другому). Объявим этот класс как public abstract, так как именно от этого класса будут наследоваться в дальнейшем все тестовые классы. Также к классу необходимо добавить атрибут [TestFixture], что обозначает класс как тест-класс. В данном классе объявляются те поля и методы, которые необходимы для прохождения любого теста.
В этом классе обязательно объявляем поле:
protected IWebDriver Driver; — это экземпляр драйвера браузера, который создается перед прогоном теста(-ов). Существует 4 вида драйверов (+ несколько в стадии разработки): FirefoxDriver, InternetExplorerDriver, ChromeDriver, HtmlUnitDriver (кроссплатформенный виртуальный браузер без графической составляющей).

Также в классе объявим 4 метода (хотя некоторые могут быть пустыми или\и не определятся вообще):
public void TestInitialize() с атрибутом [TestFixtureSetUp] – метод с данным атрибутом будет вызываться один раз перед прогоном тестов из какого-либо тестового класса (если классов несколько, то метод будет вызываться перед тестами из первого класса, потом перед методами второго класса и так далее). В этом классе желательно присвоить переменной Driver конкретное значение, например:
Driver = new FirefoxDriver();

public void TestCleanup() с атрибутом [TestFixtureTearDown] – принцип работы как у предыдущего метода, но только после прогона тест-класса. В данном классе желательно закрыть драйвер браузера, так как при прогоне следующего тест-класса обязательно создастся новый экземпляр драйвера браузера (в итоге могут остаться открытыми многие экземпляры драйвера браузера, а оперативная память не резиновая):
Driver.Quit();

public void OneTearDown() с атрибутом [TearDown] – метод вызывается после прогона каждого отдельного теста. В этом методе, например, можно почистить куки браузера перед каждым новым тестом:
Driver.Manage().Cookies.DeleteAllCookies();

public void OneSetUp() с атрибутом [SetUp] – метод вызывается перед прогоном каждого отдельного теста. Например, можно разворачивать окно браузера на весь экран при прогоне теста (неактуально для HtmlUnitDriver):
Driver.Manage().Window.Maxmize();

Обёртка

При написании разных тестов (даже в разных классах) возможно дублирование кода (точнее, оно обязательно будет). Чтобы избежать нагромождения в тестовых классах, полезно создать вспомогательный класс static class Wrapper, в который и выносим в отдельные методы дублирование кода.
Большинство тестов будут начинаться с одной и той же строчки кода:
Driver.Navigate().GoToUrl(<урл>);
Поэтому логично в классе Wrapper определить поле private static string _domain, в котором пропишем нужный нам урл, а также метод для получения этого урла:
Кусочек кода
public static string GetUrl() {
_domain = "http:\\www.yandex.ru";
return _domain;
}



Разница между Wrapper.cs и SeleniumTestBase.cs в том, что в первом объявляются методы, которые вызываются внутри тестов (и даже внутри SeleniumTestBase), а во втором – методы, которые вызываются до или после тестов.
После создания вспомогательных тестов уже создаем обычные тестовые классы.

Таким образом, архитектура тестового проекта выглядит так:


Пример написания теста


Создадим новый тестовый класс и назовем его TestClass1.cs. Вначале напишем простенький код теста, далее объясним, что какая строка кода делает.
GoTo Yandex
[Test, Timeout(10000)]
public void Test1() {
Driver.Navigate().GoToUrl(Wrapper.GetUrl());
Assert.IsTrue(Driver.Title == "Яндекс");
}


[Test] – атрибут, который определяет, что данный метод является тестовым.
Timeot(int millSec) – работает только вместе с атрибутом Test – указывает максимально допустимое количество миллисекунд на прохождение теста. Если таймаут превышен, тест падает сразу (даже во время выполнения) и считается непройденным.
public void – тест должен иметь именно такой модификатор доступа и не должен возвращать никакое значение для корректной работы теста.
Driver.Navigate().GoToUrl(Wrapper.GetUrl()) — драйвер браузера откроет нам страничку Яндекса.
Assert.IsTrue(Driver.Title == «Яндекс») — непосредственная проверка, в данном случае тест проверяет, что тайтл странички есть «Яндекс», в этом случае тест считается пройденным, иначе тест не пройден.

Запуск тестов


Чтобы прогнать наши тесты, нам необходима специально созданная для этого программа – NUnit (ссылка на скачивание в начале документа).
Скомпилируем наш тестовый проект. На выходе мы имеем файл типа .dll
Запустим NUnit. Далее нажмем File-Open Project. Появится окно, в котором мы должны найти и выбрать только что скомпилированную библиотеку.
После загрузки библиотеки в программу, слева мы увидим дерево тест-классов с нашими тестами. В этом дереве можно выбрать отдельный тест, класс или все классы тестов, после чего нажать кнопку Run – и тесты начнут прогоняться. Прогон можно в любой момент остановить, нажав кнопку Stop.
После прохождения тестов, их цвет будет меняться в дереве: зеленый – тест прошел, красный – тест упал, желтый – тест не пройдет по каким-либо причинам (не падение), серый – тест еще не запускался. Под кнопками находится окошко с логом упавших тестов, по которому можно выяснить, почему упал тот или иной тест.

Дополнительная информация


Наиболее быстрым драйвером браузера считается ChromeDriver. Угадайте самый медленный.

Для ChromeDriver и InternetExplorerDriver необходимо скачать дополнительно приложение драйвера и положить его в директорию с проектом.

Также в тестах используются атрибуты:
[TestCase(var v1, …, var vOver9000)] – используется, если тест должен перебрать несколько возможных данных входа. Перед тестом можно написать любое количество тест-кейсов. Для корректной работы тест-методу необходимо передавать столько параметров, сколько переменных прописано в тест-кейсе. Тест-кейсы должны быть идентичными по составу (нельзя перед тестом писать тест-кейсы с 1 переменной, а потом с 2-мя). Пример:
TestCase()
[Test, Timeout(10000)]
[TestCase("день", 1)]
[TestCase("дня", 2)]
[TestCase("дня", 3)]
[TestCase("дня", 4)]
[TestCase("дней", 5)]
public void Test2(string str, int days) { 
/*один тест превратился в 5
и на каждый тест отведено по 10 секунд*/}


Альтернативой тест-кейсам являются атрибуты [Range()] и [Values()]. Но в отличие от тест-кейсов эти атрибуты пишутся вместе с передаваемыми аргументами. Range лучше использовать для перечисляемых величин, Values – для не перечисляемых. Также, количество прогоняемых тестов будет равно количеству всех сочетаний переменных. Пример:
Range + Values
[Test]
public void Test3([Range(0,3)]int a, [Values(“0”, “1”, “2”, “3”)]string b) {
	Assert.IsTrue(a.ToString() == b);
}


Здесь 16 тестов и только 4 из них пройдут.

Советы


В написании автоматизированных тестов следует придерживаться концепции AAA – Arrange-Act-Assert:
Arrange – необходимые настройки и действия перед самим тестом;
Act – непосредственно тест;
Assert – блок проверки условий типа «ожидаемый результат == текущий результат».
Возьмем пример выше, немного его переписав:
AAA Begin
[Test, Timeout(10000)]
public void Test1() {
Driver.Navigate().GoToUrl(Wrapper.GetUrl());
var title =  Driver.Title;
Assert.IsTrue(title == "Яндекс"); 
}


Здесь Arrange – это переход по урлу, Act – вычисление титла страница, Assert – проверка условия «ожидаемый результат == текущий результат».
Для удобства чтения тест можно переписать так:
AAA Complete
[Test, Timeout(10000)]
public void Test1() {
//Note : Arrange
Driver.Navigate().GoToUrl("http:\\www.yandex.ru");
//Note : Act
var title =  Driver.Title;
//Note : Assert
Assert.IsTrue(title == "Яндекс"); 
}



Возьмите в привычку: один тест – один Assert. Желательно не нарушать совсем или нарушать в крайних случаях (при большом количестве проверок в тесте читабельность кода снижается).

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

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

По стандартам качества, таймаут на загрузку страницы в браузере равен 10 секунд. Ставьте таймаут по поиску элемента на странице в 11 секунд (максимум 12).

С некоторыми элементами на странице невозможно взаимодействовать при помощи Selenium + C#. В данном случае следует использовать JavaScript + JQuery для доступа к таким элементам страницы. Пример:
JavaScript + JQuery
protected IJavaScriptExecutor _jsExecutor;
var script = @"var c = $('table>tbody:visible').last();" +
                         "c = c.find(\"tr:nth-child(2)\");" +
                         "c.find(\"td:nth-child(1)\").click();";
_jsExecutor = (IJavaScriptExecutor)Driver;
_jsExecutor.ExecuteScript(script);



Для удобства прогона тестов (NUnit не самая удобная вещь) можно дополнительно установить в Visual Studio плагин ReSharper, который с легкостью позволяет запускать тесты, а также смотреть результаты прогона. Но в отличие от NUnit, ReSharper – платное дополнение.

Заключение


Данную статью, к сожалению, нельзя считать руководством по написанию Selenium-тестов, так как здесь не описаны методы и способы тестирования Web-приложений. В частности, класс Assert для проверки условий и Selenium-методы поиска элементов на Web-странице. Этому будет посвящена другая статья.
Надеюсь, что изложенный мною подход будет использован кем-либо для написания своих автоматизированных тестов.
Спасибо за внимание!

Tags:
Hubs:
+6
Comments2

Articles