Запись тестов пользовательского интерфейса
В этом разделе описано, как Энди и Амита написать тесты Selenium, которые проверяют поведение пользовательского интерфейса, описанное Амитой.
Амита обычно выполняет тесты в Chrome, Firefox и Microsoft Edge. Здесь вы делаете то же самое. Используемый вами агент, размещенный корпорацией Майкрософт, предварительно настроен для работы с каждым из этих браузеров.
Извлечение ветви из GitHub
В этом разделе вы получите selenium
ветвь из GitHub. Затем вы можете извлечь или переключиться на эту ветвь. Содержимое ветви поможет вам следовать вместе с тестами, которые Энди и Амита пишут.
Эта ветвь содержит проект Space Game , с которым вы работали в предыдущих модулях. Она также содержит конфигурацию Azure Pipelines для начала.
В Visual Studio Code откройте интегрированный терминал.
Чтобы скачать ветвь с именем
selenium
из репозитория Майкрософт, перейдите к этой ветви и выполните следующиеgit fetch
команды:git checkout
git fetch upstream selenium git checkout -B selenium upstream/selenium
Совет
Если вы последовали вместе с ручным тестом Амиты в предыдущем уроке, возможно, вы уже выполнили эти команды. Если вы уже запустили их в предыдущем уроке, вы все еще можете запустить их снова сейчас.
Помните, что вышестоящее руководство относится к репозиторию Microsoft GitHub. Конфигурация Git проекта понимает вышестоящий удаленный, так как вы настроили эту связь. Вы настроили его при создании проекта из репозитория Майкрософт и клонировали его локально.
Через некоторое время вы отправите эту ветвь в репозиторий GitHub, известный как
origin
.При необходимости в Visual Studio Code откройте файл azure-pipelines.yml . Ознакомьтесь с начальной конфигурацией.
Конфигурация напоминает те, которые вы создали в предыдущих модулях в этом пути обучения. Она предназначена для сборки только конфигурации выпуска приложения. Для краткости он также исключает триггеры, утверждения вручную и тесты, настроенные в предыдущих модулях.
Примечание.
Более надежная конфигурация может указывать ветви, участвующие в процессе сборки. Например, чтобы помочь проверить качество кода, можно запускать модульные тесты каждый раз при отправке изменений в любой ветви. Вы также можете развернуть приложение в среде, которая выполняет более исчерпывающее тестирование. Но это развертывание выполняется только при наличии запроса на вытягивание, когда у вас есть релиз-кандидат или когда вы выполняете слияние кода с главной версией.
Дополнительные сведения см. в разделе "Реализация рабочего процесса кода в конвейере сборки" с помощью триггеров конвейера сборки и GitHub.
Написание кода модульного теста
Амита рада научиться писать код, который управляет веб-браузером.
Она и Энди будут работать вместе, чтобы написать тесты Selenium. Энди уже настроил пустой проект NUnit. На протяжении всего процесса они ссылаются на документацию Selenium, несколько онлайн-учебников и заметки, которые они приняли, когда Амита сделала тесты вручную. В конце этого модуля вы найдете дополнительные ресурсы, которые помогут вам пройти процесс.
Давайте рассмотрим процесс, который Энди и Амита используют для написания своих тестов. Вы можете продолжить, открыв HomePageTest.cs в каталоге Tailspin.SpaceGame.Web.UITests в Visual Studio Code.
Определение класса HomePageTest
Энди: Первое, что нам нужно сделать, — определить наш тестовый класс. Мы можем следовать одному из нескольких соглашений об именовании. Давайте вызовем наш класс HomePageTest
. В этом классе мы поместим все наши тесты, относящиеся к домашней странице.
Энди добавляет этот код в HomePageTest.cs:
public class HomePageTest
{
}
Энди: Нужно пометить этот класс как public
, чтобы он был доступен платформе NUni.
Добавление переменной члена IWebDriver
Энди: Далее нам нужна IWebDriver
переменная-член. IWebDriver
— это интерфейс программирования, используемый для запуска веб-браузера и взаимодействия с содержимым веб-страницы.
Амита: Я слышал о интерфейсах в программировании. Можете ли вы сказать мне больше?
Энди: рассмотрим интерфейс как спецификацию или схему поведения компонента. Интерфейс предоставляет методы или поведение этого компонента. Но интерфейс не предоставляет никаких базовых сведений. Вы или кто-то другой создадите один или несколько конкретных классов , реализующих этот интерфейс. Selenium предоставляет конкретные классы, которые нам нужны.
На этой схеме IWebDriver
показан интерфейс и несколько классов, реализующих этот интерфейс:
На схеме показаны три метода, которые IWebDriver
предоставляют: Navigate
, FindElement
и Close
.
Три класса, показанные здесь, ChromeDriver
FirefoxDriver
и , и EdgeDriver
каждый из них реализует IWebDriver
и его методы. Существуют и другие классы, такие как SafariDriver
, которые также реализуют IWebDriver
. Каждый класс драйвера может управлять веб-браузером, который он представляет.
Энди добавляет переменную-член, именуемую driver
классом, как в следующем коде HomePageTest
:
public class HomePageTest
{
private IWebDriver driver;
}
Определение тестовых светильников
Энди: Мы хотим запустить весь набор тестов на Chrome, Firefox и Edge. В NUnit мы можем использовать тестовые светильники для выполнения всего набора тестов несколько раз, один раз для каждого браузера, на который мы хотим протестировать.
В NUnit атрибут используется TestFixture
для определения тестовых светильников. Энди добавляет эти три тестовых светильника в HomePageTest
класс:
[TestFixture("Chrome")]
[TestFixture("Firefox")]
[TestFixture("Edge")]
public class HomePageTest
{
private IWebDriver driver;
}
Энди: Далее необходимо определить конструктор для нашего тестового класса. Конструктор вызывается, когда NUnit создает экземпляр этого класса. В качестве аргумента конструктор принимает строку, которую мы подключили к нашим тестовым светильникам. Вот как выглядит код:
[TestFixture("Chrome")]
[TestFixture("Firefox")]
[TestFixture("Edge")]
public class HomePageTest
{
private string browser;
private IWebDriver driver;
public HomePageTest(string browser)
{
this.browser = browser;
}
}
Энди: Мы добавили browser
переменную-член, чтобы использовать текущее имя браузера в коде установки. Давайте напишем следующий код установки.
Определение метода setup
Энди: Далее необходимо назначить переменную-член IWebDriver
экземпляру класса, реализующего этот интерфейс для браузера, на который мы тестируем. FirefoxDriver
Классы ChromeDriver
, и EdgeDriver
классы реализуют этот интерфейс для Chrome, Firefox и Edge соответственно.
Создадим метод с именем Setup
, который задает driver
переменную. Мы используем OneTimeSetUp
атрибут, чтобы сообщить NUnit запустить этот метод один раз на тестовый светильник.
[OneTimeSetUp]
public void Setup()
{
}
В методе Setup
можно использовать switch
инструкцию, чтобы назначить driver
переменную-член соответствующей конкретной реализации на основе имени браузера. Теперь добавим этот код.
// Create the driver for the current browser.
switch(browser)
{
case "Chrome":
driver = new ChromeDriver(
Environment.GetEnvironmentVariable("ChromeWebDriver")
);
break;
case "Firefox":
driver = new FirefoxDriver(
Environment.GetEnvironmentVariable("GeckoWebDriver")
);
break;
case "Edge":
driver = new EdgeDriver(
Environment.GetEnvironmentVariable("EdgeWebDriver"),
new EdgeOptions
{
UseChromium = true
}
);
break;
default:
throw new ArgumentException($"'{browser}': Unknown browser");
}
Конструктор для каждого класса драйверов принимает необязательный путь к программному обеспечению Selenium драйвера, который должен управлять веб-браузером. Далее мы обсудим роль переменных среды, показанных здесь.
В этом примере EdgeDriver
конструктор также требует дополнительных параметров, чтобы указать, что мы хотим использовать версию Chromium Edge.
Определение вспомогательных методов
Энди: Я знаю, что нам потребуется повторить два действия на протяжении всех тестов:
- Поиск элементов на странице, таких как ссылки, которые мы щелкаем, и модальные окна, которые мы ожидаем отображать
- Щелкнув элементы на странице, например ссылки, раскрывающие модальные окна и кнопку, закрывающую каждый модальный модал
Давайте напишем два вспомогательных метода, по одному для каждого действия. Начнем с метода, который находит элемент на странице.
Написание вспомогательного метода FindElement
При обнаружении элемента на странице обычно это происходит в ответ на другое событие, например загрузку страницы или ввод сведений пользователем. Selenium предоставляет WebDriverWait
класс, который позволяет ждать, пока условие должно быть истинным. Если условие не соответствует заданному периоду времени, WebDriverWait
вызывает исключение или ошибку. Класс можно использовать WebDriverWait
для ожидания отображения заданного элемента и готовности к получению входных данных пользователем.
Чтобы найти элемент на странице, используйте By
класс. Класс By
предоставляет методы, позволяющие найти элемент по имени, по имени класса CSS, по тегу HTML или в нашем случае по его id
атрибуту.
Энди и Амита код до вспомогательного FindElement
метода. Этот код выглядит следующим образом:
private IWebElement FindElement(By locator, IWebElement parent = null, int timeoutSeconds = 10)
{
// WebDriverWait enables us to wait for the specified condition to be true
// within a given time period.
return new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutSeconds))
.Until(c => {
IWebElement element = null;
// If a parent was provided, find its child element.
if (parent != null)
{
element = parent.FindElement(locator);
}
// Otherwise, locate the element from the root of the DOM.
else
{
element = driver.FindElement(locator);
}
// Return true after the element is displayed and is able to receive user input.
return (element != null && element.Displayed && element.Enabled) ? element : null;
});
}
Написание вспомогательного метода ClickElement
Энди: Далее давайте напишем вспомогательный метод, который щелкает ссылки. Selenium предоставляет несколько способов записи этого метода. Одним из них является IJavaScriptExecutor
интерфейс. С его помощью мы можем программно щелкнуть ссылки с помощью JavaScript. Этот подход хорошо подходит, так как он может щелкнуть ссылки без первого прокрутки их в режиме просмотра.
ChromeDriver
, FirefoxDriver
и EdgeDriver
каждая реализация IJavaScriptExecutor
. Необходимо привести драйвер к этому интерфейсу, а затем вызвать ExecuteScript
запуск метода JavaScript click()
в базовом HTML-объекте.
Энди и Амита код до вспомогательного ClickElement
метода. Этот код выглядит следующим образом:
private void ClickElement(IWebElement element)
{
// We expect the driver to implement IJavaScriptExecutor.
// IJavaScriptExecutor enables us to execute JavaScript code during the tests.
IJavaScriptExecutor js = driver as IJavaScriptExecutor;
// Through JavaScript, run the click() method on the underlying HTML object.
js.ExecuteScript("arguments[0].click();", element);
}
Амита: Мне нравится идея добавления этих вспомогательных методов. Они, кажется, достаточно общие, чтобы использовать почти в любом тесте. Мы можем добавить дополнительные вспомогательные методы позже, так как нам нужны.
Определение метода теста
Энди: Теперь мы готовы определить метод теста. На основе тестов вручную, которые мы выполнили ранее, давайте вызовем этот метод ClickLinkById_ShouldDisplayModalById
. Рекомендуется дать описательным именам методов тестирования, определяющим точное выполнение теста. Здесь мы хотим щелкнуть ссылку, определенную его id
атрибутом. Затем мы хотим убедиться, что отображается правильное модальное окно, а также с помощью его id
атрибута.
Энди добавляет начальный код для метода тестирования:
public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{
}
Энди: Прежде чем добавить дополнительный код, давайте определим, что должен сделать этот тест.
Амита: Я могу справиться с этой частью. Наши задачи:
- Найдите ссылку по его
id
атрибуту и щелкните ссылку. - Найдите результирующий модал.
- Закройте модал.
- Убедитесь, что модал был успешно отображен.
Энди: Здорово. Нам также потребуется выполнить несколько других действий. Например, мы должны игнорировать тест, если драйвер не удалось загрузить, и нам нужно закрыть модал только в том случае, если модал был успешно отображен.
После того как перезаполнить свои кофейные кружки, Энди и Амита добавьте код в их метод тестирования. Они используют вспомогательные методы, которые они написали, чтобы найти элементы страницы и щелкнуть ссылки и кнопки. Ниже приведен результат:
public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{
// Skip the test if the driver could not be loaded.
// This happens when the underlying browser is not installed.
if (driver == null)
{
Assert.Ignore();
return;
}
// Locate the link by its ID and then click the link.
ClickElement(FindElement(By.Id(linkId)));
// Locate the resulting modal.
IWebElement modal = FindElement(By.Id(modalId));
// Record whether the modal was successfully displayed.
bool modalWasDisplayed = (modal != null && modal.Displayed);
// Close the modal if it was displayed.
if (modalWasDisplayed)
{
// Click the close button that's part of the modal.
ClickElement(FindElement(By.ClassName("close"), modal));
// Wait for the modal to close and for the main page to again be clickable.
FindElement(By.TagName("body"));
}
// Assert that the modal was displayed successfully.
// If it wasn't, this test will be recorded as failed.
Assert.That(modalWasDisplayed, Is.True);
}
Амита: Код выглядит отлично до сих пор. Но как подключить этот тест к атрибутам id
, собранным ранее?
Энди: Большой вопрос. Мы обработаем следующее.
Определение данных тестового варианта
Энди: В NUnit можно предоставить данные для тестов несколькими способами. Здесь мы используем TestCase
атрибут. Этот атрибут принимает аргументы, которые позже передаются обратно в метод тестирования при запуске. У нас может быть несколько TestCase
атрибутов, каждый из которых тестирует другую функцию нашего приложения. Каждый TestCase
атрибут создает тестовый случай, включенный в отчет, который отображается в конце выполнения конвейера.
Энди добавляет эти TestCase
атрибуты в метод тестирования. Эти атрибуты описывают кнопку "Скачать игру", один из экранов игры и лучший игрок в списке лидеров. Каждый атрибут задает два id
атрибута: один для ссылки, чтобы щелкнуть и один для соответствующего модального окна.
// Download game
[TestCase("download-btn", "pretend-modal")]
// Screen image
[TestCase("screen-01", "screen-modal")]
// // Top player on the leaderboard
[TestCase("profile-1", "profile-modal-1")]
public void ClickLinkById_ShouldDisplayModalById(string linkId, string modalId)
{
...
Энди: Для каждого атрибута TestCase
первым параметром является атрибут id
нажимаемой ссылки. Второй параметр — это id
атрибут модального окна, который мы ожидаем увидеть. Эти параметры соответствуют двухстроковым аргументам в нашем методе тестирования.
Амита: Я вижу это. С какой-то практикой, я думаю, что могу добавить свои собственные тесты. Когда эти тесты выполняются в нашем конвейере?
Энди: Прежде чем отправлять изменения через конвейер, сначала давайте убедимся, что код компилируется и выполняется локально. Мы зафиксируем и отправим изменения в GitHub и увидим их перемещение через конвейер только после проверки работы всего. Давайте будем выполнять тесты локально.