[Из песочницы] Смок-тестирование релиз-кандидата автотестами за 15 минут

Меня зовут Лилия, я QA Lead в одном из проектов финансовой группы БКС (сервис по подбору выгодных для клиента предложений из ряда кредитных продуктов), и сегодня я расскажу, как мы автоматизировали смок-тестирование, с какими проблемами столкнулись и какой стек технологий используем.

Сначала мы решили автоматизировать регресс-тестирование, но время шло, функциональность менялась и мы поняли, что довольно много времени тратится на поддержку уже написанных автотестов. Поэтому решили автоматизировать сначала смок-тест, а затем уже расширять его до автоматического проведения регрессионного тестирования. Перед отделом тестирования была поставлена задача в максимально сжатые сроки произвести автоматизацию смок-тестирования, т.к. проект продолжал расти и обрастать дополнительными функциями.

Что такое смок-тестирование


Смок-тестирование, как его еще называют «дымовое тестирование» — быстрая проверка наиболее критичной функциональности.

На нашем проекте:

  • Регистрация/авторизация.
  • Вход.
  • Заполнение анкеты.
  • Витрина предложений.
  • Отправка заявки/переход по ссылке на сайт партнера.
  • Обратная связь.
  • Блокировка.


Стек технологий для написания автотестов


Автотесты мы пишем на вот таком стеке: Java + Selenium + Cucumber + отчеты в Allure2.

image

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

image

Настройка CI


Пока мы писали автотесты, у финансовой группы БКС появился Selenoid, и мы смогли настроить запуск тестов в pipline GitLab

Организация написания автотестов для разных стендов


У нас есть несколько стендов, на которых и происходит разработка, отладка, приемка, а еще есть очень много feature-стендов, где мы тестируем новые функции, разрабатываемые распределенными командами.

Так же у нас есть несколько стендов веток, которые соответствуют разным средам разработки, при изменении файлов на стенде происходит автоматический запуск соответствующего стенда с автотестами.

Итого


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

© Habrahabr.ru