Сначала мы решили автоматизировать регресс-тестирование, но время шло, функциональность менялась и мы поняли, что довольно много времени тратится на поддержку уже написанных автотестов. Поэтому решили автоматизировать сначала смок-тест, а затем уже расширять его до автоматического проведения регрессионного тестирования. Перед отделом тестирования была поставлена задача в максимально сжатые сроки произвести автоматизацию смок-тестирования, т.к. проект продолжал расти и обрастать дополнительными функциями.
Что такое смок-тестирование
Смок-тестирование, как его еще называют «дымовое тестирование» – быстрая проверка наиболее критичной функциональности.
На нашем проекте:
- Регистрация/авторизация.
- Вход.
- Заполнение анкеты.
- Витрина предложений.
- Отправка заявки/переход по ссылке на сайт партнера.
- Обратная связь.
- Блокировка.
Стек технологий для написания автотестов
Автотесты мы пишем на вот таком стеке: Java + Selenium + Cucumber + отчеты в Allure2.
BDD Автотесты для смок-тестирования
1. Фича файл с расширением .feature с описанием сценариев тестов на языке Gerkin.
Пример:
Функция: Проверка профиля, личного кабинета и заявок @all Сценарий: Проверка блоков на странице профиля Дано Перейти на страницу Главная в приложении Когда Главная: Нажимаю на ссылка Войти И Вход: Ввожу в поле Телефон значение ********** И Вход: Нажимаю на кнопка Получить код И Вход: Ввожу в поле Код подтверждения значение **** И Главная: Навожу курсор на элемент ссылка Меню пользователя И Главная: Нажимаю на ссылка Профиль Тогда Происходит переход на страницу Профиль Тогда Профиль: Отображается элемент заголовок Регистрационные данные Тогда Профиль: Отображается элемент заголовок Паспортные данные Тогда Профиль: Отображается элемент заголовок Адрес регистрации Тогда Профиль: Отображается элемент заголовок Адрес проживания Тогда Профиль: Отображается элемент заголовок Сведения о трудоустройстве
2. Шаги steps. В нем находятся классы, в которых описаны действия с элементами на странице и проверки этих элементов.
Пример:
@When("^Нажимаю клавишу (.*)") public void pressKey(String key) { webElementUtils.pressKey(key); } @When("^(.*): Нажимаю на (.*)") public void press(String pageTitle, String elementName) { waitUtils.waitElementToBeClickable(getWebElementOnWebPageWithWaiter(elementName, pageTitle)).click(); } @When("^(.*): Ставлю отметку на (.*)") public void checkCheckbox(String pageTitle, String elementName) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); if (!webElementUtils.isCheckboxSelected(element)) { element.click(); } } @When("^(.*): Снимаю отметку с (.*)") public void uncheckCheckbox(String pageTitle, String elementName) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); if (webElementUtils.isCheckboxSelected(element)) { element.click(); } } @And("^(.*): Стираем значение в (.*)$") public void erase(String pageTitle, String elementName) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); webElementUtils.clearElement(element); } @And("^(.*): Ввожу в (.*) значение (.*)$") public void enterValue(String pageTitle, String elementName, String text) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); webElementUtils.fillElementWithText(element, expressionUtils.parseString(text)); } @And("^(.*): в (.*) выбрать (.*)$") public void selectValue(String pageTitle, String dropdownListName, String value) { WebElement element = getWebElementOnWebPageWithWaiter(dropdownListName, pageTitle); webElementUtils.selectValueFromCombobox(element, value); } @Then("^(.*): в (.*) отсутствует текст$") public void elementDoesNotContainAnyText(String pageTitle, String elementName) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); assertEquals("", webElementUtils.getTextFromWebElement(element).trim()); } @Then("^(.*): ползунок (.*) находится в положении (.*)$") public void checkSliderPosition(String pageTitle, String elementName, String expectedPosition) { WebElement element = getWebElementOnWebPageWithWaiter(elementName, pageTitle); String sliderTrackPosition = StringUtils.substringBetween(element.findElement(By.cssSelector(".rc-slider-track")).getAttribute("style"), "width: ", ";"); String sliderHandlePosition = StringUtils.substringBetween(element.findElement(By.cssSelector(".rc-slider-handle")).getAttribute("style"), "left: ", ";"); assertEquals(expectedPosition, sliderTrackPosition); assertEquals(expectedPosition, sliderHandlePosition); } @Then("^(.*): Отображается компонент (.*)$") public void checkComponentIsDisplayed(String pageTitle, String component) { WebElement element = getWebElementOnWebPageWithWaiter(component, component); assertTrue(webElementUtils.isElementVisible(element)); } @When("^(.*): Загружаю файл (.+) в (.*)$") public void loadFileInField(String pageTitle, String fileName, String elementName) { WebElement element = getWebElementOnWebPage(elementName, pageTitle); File file = new File(Objects.requireNonNull(getClass().getClassLoader().getResource(fileName)).getFile()); element.sendKeys(file.getAbsolutePath()); } @Then("^(.+): У элемента (.+) атрибут (.+) имеет значение (.+)$") public void checkAttributeInElement(String pageTitle, String elementName, String attributeName, String expectedValue) { WebElement element = getWebElementOnWebPage(elementName, pageTitle); String attribute = webElementUtils.getAttribute(element, attributeName); String message = String.format("Атрибут '%s' у элемента '%s' на странице '%s' не соответствует ожидаемому значению.\n" + "Ожидаемое значение: '%s'.\nФактическое значение: '%s'.\n", attributeName, elementName, pageTitle, expectedValue, attribute); assertEquals(message, expectedValue, attribute); } @Then("^(.+): У элемента (.+) значение (.+)/$") public void checkValueTag(String pageTitle, String tagName, String expectedValue) { WebElement title = webDriver.findElement(By.tagName(tagName)); assertEquals(expectedValue, title.getAttribute("innerHTML").trim()); } }
3. Работа с локаторами на страницах (паттерн PageObject)
Пример:
import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import ru.bcs.creditmarkt.acceptance.pageobject.annotation.PageObject; import ru.yandex.qatools.htmlelements.annotations.Name; @PageObject(title = "Вход", path = "/entry/login") public class LoginPage extends WebPage { @Name("ссылка Регистрация") @FindBy(xpath = "//a[text()='Регистрация']") private WebElement registrationLink; @Name("ссылка Вход") @FindBy(xpath = "//a[text()='Вход']") private WebElement loginLink; @Name("поле Телефон") @FindBy(css = "#phone") private WebElement phoneInput; @Name("кнопка Получить код") @FindBy(css = "button[type=submit]") private WebElement receiveCodeButton; @Name("поле Код подтверждения") @FindBy(css = "input#sms") private WebElement smsInput; @Name("чекбокс Согласен с условиями обработки персональных данных") @FindBy(css = "button#personalAgreement") private WebElement personalAgreementCheckbox; @Name("иконка Чат-бот") @FindBy(css = "div.wa-userpic") private WebElement chatBotIcon; }
4. Отчет в Allure2
Настройка CI
Пока мы писали автотесты, у финансовой группы БКС появился Selenoid, и мы смогли настроить запуск тестов в pipline GitLab
Организация написания автотестов для разных стендов
У нас есть несколько стендов, на которых и происходит разработка, отладка, приемка, а еще есть очень много feature-стендов, где мы тестируем новые функции, разрабатываемые распределенными командами.
Так же у нас есть несколько стендов веток, которые соответствуют разным средам разработки, при изменении файлов на стенде происходит автоматический запуск соответствующего стенда с автотестами.
Итого
Сейчас в нашем проекте при публикации релиза на приемочный стенд за 15 минут в автоматическом режиме выполняется полный набор смок-тестов. В зависимости от полученных результатов команда тестирования принимает решение о приемке релиз-кандидата в тестирование для регрессионного тестирования.
ссылка на оригинал статьи https://habr.com/ru/post/459734/