[Тестирование IT-систем, Программирование, Тестирование веб-сервисов, Управление разработкой] Зачем нам вулканец на борту: обзор Spock Framework
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Автоматизация тестирования помогает постоянно контролировать качество IT-продукта, а также снижать затраты в долгосрочной перспективе. В автоматизации существуют различные подходы, например, Behavior Driven Development (BDD), разработка через поведение.
С этим подходом связаны инструменты cucumber, robot framework, behave и другие, в которых разделены сценарии выполнения и реализация каждой конструкции. Такое разделение помогает составить удобочитаемые сценарии, но требует значительных затрат времени и поэтому может быть непрактичным при написании реализации.
Рассмотрим, как можно упростить работу с BDD, используя подходящие инструменты – например, фреймворк Spock, который сочетает в себе красоту, удобство принципов BDD и особенности jUnit.
Spock framework
Spock – фреймворк для тестирования и спецификации приложений на языках Java и Groovy. Благодаря использованию в качестве основы платформы JUnit этот фреймворк совместим со всеми популярными IDE (в частности, IntelliJ IDEA), различными инструментами сборки (Ant, Gradle, Maven) и continuous integration (CI) серверами.
Как пишут разработчики фреймворка, Spock «вдохновлен JUnit, RSpec, jMock, Mockito, Groovy, Scala, вулканцами и другими увлекательными формами жизни».
В этой статье мы рассмотрим последнюю доступную версию, Spock Framework 2.0. Ее особенности: возможность использования JUnit5, Java 8+, groovy 2.5 (также существует сборка с версией 3.0). Spock распространяется по лицензии Apache 2.0 и имеет отзывчивое сообщество пользователей. Разработчики фреймворка продолжают дорабатывать и развивать Spock, который уже включает в себя множество расширений, позволяющих тщательно настроить запуск тестов. Например, одно из наиболее интересных анонсированных направлений доработки – это добавление параллельного исполнения тестов.
Groovy
Groovy является объектно-ориентированным языком программирования, разработанным для платформы Java как дополнение с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java-кодом и библиотеками. Язык может использоваться в любом Java-проекте или как скриптовый язык.
К особенностям groovy относятся: как статическая, так и динамическая типизация; встроенный синтаксис для списков, массивов и регулярных выражений; перегрузка операций. При этом замыкания в Groovy появились задолго до Java.
Groovy хорошо подходит для быстрой разработки тестов, когда есть возможность применять «синтаксический сахар», схожий с python, и не задумываться о типизации объектов.
Особенности Spock Framework
Одна из ключевых особенностей фреймворка – у разработчика есть возможность писать спецификации с ожидаемыми характеристиками системы с использованием принципов BDD подхода. Этот подход позволяет составлять бизнес-ориентированные функциональные тесты для программных продуктов с высокой предметной и организационной сложностью.
Спецификация представляет собой класс groovy, расширяющий spock.lang.Specification
class MyFirstSpecification extends Specification {
// fields
// fixture methods
// feature methods
// helper methods
}
Спецификация может содержать различные вспомогательные поля, которые инициируются для каждого класса спецификации.
С помощью аннотации @Shared можно дать доступ к полю классам-наследникам спецификации.
abstract class PagesBaseSpec extends Specification {
@Shared
protected WebDriver driver
def setup() {
this.driver = DriverFactory.createDriver()
driver.get("www.anywebservice.ru")
}
void cleanup() {
driver.quit()
}
}
Методы настройки класса спецификации:
def setupSpec() {} // запускается при работе первого feature метода из спецификации
def setup() {} // запускается перед каждым feature методом
def cleanup() {} // запускается после каждого feature метода
def cleanupSpec() {} // запускается после работы последнего feature метода из спецификации
В следующей таблице рассмотрим, у каких ключевых слов и методов Spock framework есть аналоги в JUnit.
Блоки теста
В Spock Framework каждая фаза теста выделена в отдельный блок кода (см. пример в документации).
Блок кода начинается с лейбла и завершается началом следующего блока кода или окончанием теста.
Блок given отвечает за настройку начальных условий теста.
Блоки when, then всегда используются вместе. В блоке when – стимулятор, раздражитель системы, а в блоке then – ответная реакция системы.
В тех случаях, когда есть возможность сократить конструкцию when-then до одного выражения, можно использовать один блок expect. Далее будут использованы примеры из официальной документации Spock framework:
when:
def x = Math.max(1, 2)
then:
x == 2
или одно выражение
expect:
Math.max(1, 2) == 2
Блок cleanup применяют для освобождения ресурсов перед следующей итерацией теста.
given:
def file = new File("/some/path")
file.createNewFile()
// ...
cleanup:
file.delete()
Блок where применяют для передачи данных для тестирования (Data Driven Testing).
def "computing the maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a << [5, 3]
b << [1, 9]
c << [5, 9]
}
Виды передачи входных данных будут рассмотрены далее.
Пример реализации теста на Spock Framework
Далее рассмотрим подходы к реализации тестирования веб-страницы авторизации пользователя в системе с использованием selenium.
import helpers.DriverFactory
import org.openqa.selenium.WebDriver
import spock.lang.Shared
import spock.lang.Specification
abstract class PagesBaseSpec extends Specification {
@Shared
protected WebDriver driver
def setup() {
this.driver = DriverFactory.createDriver()
driver.get("www.anywebservice.ru")
}
void cleanup() {
driver.quit()
}
}
Здесь мы видим базовый класс спецификации страницы. В начале класса мы видим импорт необходимых классов. Далее представлена аннотация shared, позволяющая классам-наследникам получить доступ к веб-драйверу. В блоке setup() мы видим код инициализации веб-драйвера и открытия веб-страницы. В блоке cleanup() – код завершения работы веб-драйвера.
Далее перейдем к обзору спецификации страницы авторизации пользователя.
import pages.LoginPage
import spock.lang.Issue
class LoginPageTest extends PagesBaseSpec {
@Issue("QAA-1")
def "QAA-1: Authorization with correct login and password"() {
given: "Login page"
def loginPage = new LoginPage(driver)
and: "Correct login and password"
def adminLogin = "adminLogin"
def adminPassword = "adminPassword"
when: "Log in with correct login and password"
loginPage.login(adminLogin, adminPassword)
then: "Authorized and moved to main page"
driver.currentUrl == "www.anywebservice.ru/main"
}
}
Спецификация страницы авторизации наследуется от базовой спецификации страниц. Аннотация Issue задает идентификатор теста во внешней системе трекинга (например, Jira). В следующей строке мы видим название теста, которое по соглашению задается строковыми литералами, что позволяет использовать любые символы в названии теста (в том числе и русскоязычные). В блоке given происходит инициализация page object класса страницы авторизации, а также получение корректных логина и пароля для авторизации в системе. В блоке when выполняется действие по авторизации. В блоке then – проверка ожидаемого действия, а именно – успешная авторизация и переадресация на главную страницу системы.
На примере данной спецификации мы видим наиболее значимый плюс использования парадигмы BDD в spock – спецификация системы одновременно является и ее документацией. Каждый тест описывает определенное поведение, каждый шаг в тесте имеет свое описание, понятное не только разработчикам, но и заказчикам. Описание блоков может быть представлено не только в исходном коде теста, но и в диагностических сообщениях или отчетах о работе теста.
В фреймворке предусмотрена возможность передавать различные логины и пароли для тестирования (параметризировать тест).
Data Driven Testing в Spock Framework
Data Driven Testing = table-driven testing = parameterized testing
Для тестирования сценария с несколькими параметрами можно использовать различные варианты их передачи.
Таблицы данных (Data Tables)
Рассмотрим несколько примеров из официальной документации фреймворка.
class MathSpec extends Specification {
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b | c
1 | 3 | 3
7 | 4 | 7
0 | 0 | 0
}
}
Каждая строка в таблице – отдельная итерация теста. Также таблица может быть представлена и одним столбцом.
where:
a | _
1 | _
7 | _
0 | _
_ — объект-заглушка класса спецификации.
Для лучшего визуального восприятия параметров можно переписать пример выше в следующем виде:
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
a | b || c
1 | 3 || 3
7 | 4 || 7
0 | 0 || 0
}
Теперь мы видим, что a, b – входные параметры, а c – ожидаемое значение.
Потоки данных (Data pipes)
В некоторых случаях использование таблицы параметров будет выглядеть очень громоздко. В таких случаях можно применять следующий вид передачи параметров:
...
where:
a << [1, 7, 0]
b << [3, 4, 0]
c << [3, 7, 0]
Здесь левый сдвиг << – перегруженный groovy оператор, который теперь выполняет роль добавления элементов в список.
Для каждой итерации теста будут запрашиваться следующие данные из списка для каждой переменной:
1 итерация: a=1, b=3, c=3;
2 итерация: a=7, b=4, c=7;
3 итерация: a=0, b=0, c=0.
Причем входные данные могут не только передаваться явно, но и запрашиваться при необходимости из различных источников. Например, из базы данных:
@Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
def "maximum of two numbers"() {
expect:
Math.max(a, b) == c
where:
[a, b, c] << sql.rows("select a, b, c from maxdata")
}
Переменная как данные (Data Variable Assignment)
...
where:
a = 3
b = Math.random() * 100
c = a > b ? a : b
Здесь мы видим динамически вычисляемую переменную c в тестовых данных.
Комбинация различных видов передачи параметров
...
where:
a | _
3 | _
7 | _
0 | _
b << [5, 0, 0]
c = a > b ? a : b
Никто не запрещает вам применять сразу несколько видов передачи, если это необходимо.
Пример реализации параметризованного теста на Spock Framework
@Issue("QAA-1-parametrized")
def "QAA-1-parametrized: Authorization with correct login and password"() {
given: "Login page"
def loginPage = new LoginPage(driver)
when: "Log in with correct login and password"
loginPage.login(login, password)
then: "Authorized and moved to main page"
driver.currentUrl =="www.anywebservice.ru/main"
where: "Check for different logins and passwords"
login | password
"adminLogin" | "adminPassword"
"moderatorLogin" | "moderatorPassword"
"userLogin" | "userPassword"
}
Здесь мы видим уже знакомый нам блок where, в котором заданы ключи параметров (логинов и паролей), которые хранятся в файле конфигурации.
Ввиду особенностей используемой реализации спецификации, цикл настройки веб-драйвера и его закрытие (а значит и закрытие браузера) будет выполняться для каждого параметра, что негативно скажется на времени выполнения теста. Предлагаем доработать спецификацию и улучшить время работы теста.
Пример реализации параметризованного теста с доработанной спецификацией
До доработки
abstract class PagesBaseSpec extends Specification {
@Shared
protected WebDriver driver
def setup() {
this.driver = DriverFactory.createDriver()
driver.get("www.anywebservice.ru")
}
void cleanup() {
driver.quit()
}
}
После доработки
import helpers.DriverFactory
import org.openqa.selenium.WebDriver
import spock.lang.Shared
import spock.lang.Specification
abstract class PagesNoRestartBaseSpec extends Specification {
@Shared
protected WebDriver driver
def setupSpec() {
this.driver = DriverFactory.createDriver()
}
def setup() {
this.driver.get("www.anywebservice.ru")
}
def cleanup() {
this.driver.get("www.anywebservice.ru/logout")
this.driver.manage().deleteAllCookies();
}
void cleanupSpec() {
this.driver.quit()
}
}
В обновленной спецификации мы видим, что процедура создания веб-драйвера будет выполняться только при настройке класса спецификации, а закрытие браузера – только после завершения работы тестов из спецификации. В методе setup() мы видим тот же код получения веб-адреса сервиса и его открытие в браузере, а в методе cleanup() – переход по адресу www.anywebservice.ru/logout для завершения работы с сервисом у текущего пользователя и удаления файлов куки (для тестирования текущего веб-сервиса данной процедуры достаточно, чтобы имитировать «уникальный» запуск). Код самого теста не изменился.
В итоге с помощью нехитрых доработок мы получили минимум двукратное уменьшение времени работы автотеста, по сравнению с первичной реализацией.
Сравнение тестов на testNG, pytest, pytest-bdd
Для начала мы рассмотрим реализацию теста на тестовом фреймворке testNG на языке программирования Java, который также, как и Spock Framework, вдохновлен фреймворком jUnit и поддерживает data-driven testing.
package javaTests;
import org.testng.Assert;
import org.testng.annotations.*;
import pages.LoginPage;
public class LoginPageTest extends BaseTest {
@BeforeClass
public final void setup() {
createDriver();
driver.get("www.anywebservice.ru");
}
@DataProvider(name = "userParameters")
public final Object[][] getUserData(){
return new Object[][] {
{"adminLogin", "adminPassword"},
{"moderatorLogin", "moderatorPassword"},
{"userLogin", "userPassword"}
};
}
@Test(description = "QAA-1-1: Authorization with correct login and password",
dataProvider = "userParameters")
public final void authorizationWithCorrectLoginAndPassword(String login, String password){
//Login page
LoginPage loginPage = new LoginPage(driver);
//Log in with correct login and password
loginPage.login(login, password);
//Authorized and moved to main page
Assert.assertEquals("www.anywebservice.ru/main", driver.getCurrentUrl());
}
@AfterMethod
public final void cleanup() {
driver.get("www.anywebservice.ru/logout");
driver.manage().deleteAllCookies();
}
@AfterClass
public final void tearDown() {
driver.quit();
}
}
Здесь мы можем видеть тестовый класс со всеми необходимыми setup(), cleanup() методами, а также параметризацию теста в виде дополнительного метода getUserData() с аннотацией @DataProvider, что выглядит несколько громоздко, после того, что мы рассмотрели в тесте с использованием Spock Framework. Также для понимания того, что происходит в тесте, были оставлены комментарии, аналогичные описанию шагов.
Стоит отметить, что в testNG, в отличие от Spock Framework, реализована поддержка параллельного выполнения теста.
Далее перейдем к тесту с использованием тестового фреймворка pytest на языке программирования Python.
import pytest
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from PageObjects.LoginPage import LoginPage
class TestLogin(object):
@pytest.mark.parametrize("login,password", [
pytest.param(("adminLogin", "adminPassword"), id='admin'),
pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'),
pytest.param(("userLogin", "userPassword"), id='user')
])
def test_authorization_with_correct_login_and_password(self, login, password, driver, test_cleanup):
# Login page
login_page = LoginPage(driver)
# Log in with correct login and password
login_page.login(login, password)
# Authorized and moved to main page
assert expected_conditions.url_to_be("www.anywebservice.ru/main")
@pytest.fixture()
def test_cleanup(self, driver):
yield "test"
driver.get("www.anywebservice.ru/logout")
driver.delete_all_cookies()
Здесь мы также видим поддержку data-driven testing в виде отдельной конструкции, схожей с @DataProvider в testNG. Метод настройки веб-драйвера «спрятан» в фикстуре driver. Благодаря динамической типизации и фикстурам pytest, код этого теста выглядит чище, чем на Java.
Далее перейдем к обзору кода теста с использованием плагина pytest-bdd, который позволяет писать тесты в виде feature файлов Gherkin (чистый BDD-подход).
login.feature
Feature: Login page
A authorization
Scenario: Authorizations with different users
Given Login page
When Log in with correct login and password
Then Authorized and moved to main page
test_login.py
import pytest
from pytest_bdd import scenario, given, when, then
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from PageObjects.LoginPage import LoginPage
@pytest.mark.parametrize("login,password", [
pytest.param(("adminLogin", "adminPassword"), id='admin'),
pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'),
pytest.param(("userLogin", "userPassword"), id='user')
])
@scenario('login.feature', 'Authorizations with different users')
def test_login(login, password):
pass
@given('Login page')
def login_page(driver):
return LoginPage(driver)
@when('Log in with correct login and password')
def login_with_correct_login_and_password(login_page, login, password):
login_page_object = login_page
login_page_object.login(login, password)
@then('Authorized and moved to main page')
def authorized_and_moved_to_main_page(driver, login):
assert expected_conditions.url_to_be("www.anywebservice.ru/main")
Из плюсов можно выделить то, что это все еще фреймворк pytest, который имеет множество плагинов для различных ситуаций, в том числе и для параллельного запуска тестов. Из минусов – сам чистый BDD-подход, который будет постоянно ограничивать разработчика своими особенностями. Spock Framework дает возможность писать более лаконичный и простой в оформлении код, по сравнению со связкой PyTest + pytest-bdd.
Заключение
В этой статье мы рассмотрели возможность упрощения работы с BDD с помощью фреймворка Spock. Подводя итоги, кратко выделим основные, на наш взгляд, плюсы и минусы Spock по сравнению с некоторыми другими распространенными тестовыми фреймворками.
Плюсы:
- Использование принципов BDD вместо чистого BDD-подхода дает большую гибкость при написании тестов.
- Написанная тестовая спецификация является также и документацией системы.
- Наличие различных расширений для настройки тестов.
- Язык groovy (динамическая типизация, синтаксический сахар, closures или замыкания).
Минусы:
- Динамическая типизация языка groovy. Поскольку применяется динамическая типизация, то механизмы предугадывания, используемые в IDE для анализа содержимого переменной, при долгой работе могут начать сбоить. Если рассматривать Intellij IDEA, то постоянно ведутся доработки в этом направлении, что, несомненно, радует.
- Динамическая компиляция groovy кода в JVM байт-код. Если кратко, то не стоит писать все подряд на groovy, поскольку вы можете существенно проиграть во времени компиляции данного кода, особенно если он занимает много строк кода и часто используется. Важные части своего тестового фреймворка все же стоит писать на java, а groovy оставить для тестов.
- Набор расширений не такой обширный, как у testNG, к примеру. Как следствие – отсутствие параллельного запуска тестов. Есть планы добавить эту функциональность, но сроки их реализации неизвестны.
В конечном итоге, инструмент Spock Framework, несомненно, заслуживает внимания, поскольку подходит для решения сложной распространенной задачи автоматизации бизнес-сценариев для программных продуктов с высокой предметной и организационной сложностью.
Что еще можно почитать:
- Особенности использования Spock в проектах на Kotlin
- Возможности фреймворка Spock
- Возможности фреймворка по подмене внешних зависимостей (созданию моков)
- Сравнение Junit5 и Spock 1.3
===========
Источник:
habr.com
===========
Похожие новости:
- [Тестирование IT-систем, Тестирование веб-сервисов, Тестирование мобильных приложений, Учебный процесс в IT, Карьера в IT-индустрии] Нужен ли девушкам в IT особый подход? О первой QA-школе Women in Tech в России
- [Python, Программирование] Как вернуть сразу несколько значений из функции в Python 3 (перевод)
- [Программирование, Управление разработкой, Карьера в IT-индустрии] Больше не тимлид: как не потерять себя и найти снова
- [Программирование, Анализ и проектирование систем, Облачные сервисы, Kubernetes] О растущей популярности Kubernetes (перевод)
- [Информационная безопасность, Тестирование веб-сервисов] История взлома одного интернет провайдера
- [Высокая производительность, Lua, Go, Тестирование веб-сервисов] Кто такая эта Ваша Pandora и причем здесь Tarantool
- [Программирование] Зачем нужен CI/CD и как научиться его настраивать?
- [Ненормальное программирование, Программирование, Совершенный код] Dark code-style academy: spoil if statement
- [Тестирование IT-систем, PostgreSQL, IT-инфраструктура, Администрирование баз данных] Моделирование отказоустойчивых кластеров на базе PostgreSQL и Pacemaker
- [Программирование, Хакатоны] Запускаем двухнедельный хакатон «Вездекод»
Теги для поиска: #_testirovanie_itsistem (Тестирование IT-систем), #_programmirovanie (Программирование), #_testirovanie_vebservisov (Тестирование веб-сервисов), #_upravlenie_razrabotkoj (Управление разработкой), #_avtomatizatsija_testirovanija (автоматизация тестирования), #_sdet, #_behaviour_driven_development, #_bdd, #_spock_framework, #_testing, #_spock, #_testirovanie (тестирование), #_avtotesty (автотесты), #_blog_kompanii_simbirsoft (
Блог компании SimbirSoft
), #_testirovanie_itsistem (
Тестирование IT-систем
), #_programmirovanie (
Программирование
), #_testirovanie_vebservisov (
Тестирование веб-сервисов
), #_upravlenie_razrabotkoj (
Управление разработкой
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:31
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Автоматизация тестирования помогает постоянно контролировать качество IT-продукта, а также снижать затраты в долгосрочной перспективе. В автоматизации существуют различные подходы, например, Behavior Driven Development (BDD), разработка через поведение. С этим подходом связаны инструменты cucumber, robot framework, behave и другие, в которых разделены сценарии выполнения и реализация каждой конструкции. Такое разделение помогает составить удобочитаемые сценарии, но требует значительных затрат времени и поэтому может быть непрактичным при написании реализации. Рассмотрим, как можно упростить работу с BDD, используя подходящие инструменты – например, фреймворк Spock, который сочетает в себе красоту, удобство принципов BDD и особенности jUnit. Spock framework Spock – фреймворк для тестирования и спецификации приложений на языках Java и Groovy. Благодаря использованию в качестве основы платформы JUnit этот фреймворк совместим со всеми популярными IDE (в частности, IntelliJ IDEA), различными инструментами сборки (Ant, Gradle, Maven) и continuous integration (CI) серверами. Как пишут разработчики фреймворка, Spock «вдохновлен JUnit, RSpec, jMock, Mockito, Groovy, Scala, вулканцами и другими увлекательными формами жизни». В этой статье мы рассмотрим последнюю доступную версию, Spock Framework 2.0. Ее особенности: возможность использования JUnit5, Java 8+, groovy 2.5 (также существует сборка с версией 3.0). Spock распространяется по лицензии Apache 2.0 и имеет отзывчивое сообщество пользователей. Разработчики фреймворка продолжают дорабатывать и развивать Spock, который уже включает в себя множество расширений, позволяющих тщательно настроить запуск тестов. Например, одно из наиболее интересных анонсированных направлений доработки – это добавление параллельного исполнения тестов. Groovy Groovy является объектно-ориентированным языком программирования, разработанным для платформы Java как дополнение с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java-кодом и библиотеками. Язык может использоваться в любом Java-проекте или как скриптовый язык. К особенностям groovy относятся: как статическая, так и динамическая типизация; встроенный синтаксис для списков, массивов и регулярных выражений; перегрузка операций. При этом замыкания в Groovy появились задолго до Java. Groovy хорошо подходит для быстрой разработки тестов, когда есть возможность применять «синтаксический сахар», схожий с python, и не задумываться о типизации объектов. Особенности Spock Framework Одна из ключевых особенностей фреймворка – у разработчика есть возможность писать спецификации с ожидаемыми характеристиками системы с использованием принципов BDD подхода. Этот подход позволяет составлять бизнес-ориентированные функциональные тесты для программных продуктов с высокой предметной и организационной сложностью. Спецификация представляет собой класс groovy, расширяющий spock.lang.Specification class MyFirstSpecification extends Specification {
// fields // fixture methods // feature methods // helper methods } Спецификация может содержать различные вспомогательные поля, которые инициируются для каждого класса спецификации. С помощью аннотации @Shared можно дать доступ к полю классам-наследникам спецификации. abstract class PagesBaseSpec extends Specification {
@Shared protected WebDriver driver def setup() { this.driver = DriverFactory.createDriver() driver.get("www.anywebservice.ru") } void cleanup() { driver.quit() } } Методы настройки класса спецификации: def setupSpec() {} // запускается при работе первого feature метода из спецификации
def setup() {} // запускается перед каждым feature методом def cleanup() {} // запускается после каждого feature метода def cleanupSpec() {} // запускается после работы последнего feature метода из спецификации В следующей таблице рассмотрим, у каких ключевых слов и методов Spock framework есть аналоги в JUnit. Блоки теста В Spock Framework каждая фаза теста выделена в отдельный блок кода (см. пример в документации). Блок кода начинается с лейбла и завершается началом следующего блока кода или окончанием теста. Блок given отвечает за настройку начальных условий теста. Блоки when, then всегда используются вместе. В блоке when – стимулятор, раздражитель системы, а в блоке then – ответная реакция системы. В тех случаях, когда есть возможность сократить конструкцию when-then до одного выражения, можно использовать один блок expect. Далее будут использованы примеры из официальной документации Spock framework: when:
def x = Math.max(1, 2) then: x == 2 или одно выражение expect:
Math.max(1, 2) == 2 Блок cleanup применяют для освобождения ресурсов перед следующей итерацией теста. given:
def file = new File("/some/path") file.createNewFile() // ... cleanup: file.delete() Блок where применяют для передачи данных для тестирования (Data Driven Testing). def "computing the maximum of two numbers"() {
expect: Math.max(a, b) == c where: a << [5, 3] b << [1, 9] c << [5, 9] } Виды передачи входных данных будут рассмотрены далее. Пример реализации теста на Spock Framework Далее рассмотрим подходы к реализации тестирования веб-страницы авторизации пользователя в системе с использованием selenium. import helpers.DriverFactory
import org.openqa.selenium.WebDriver import spock.lang.Shared import spock.lang.Specification abstract class PagesBaseSpec extends Specification { @Shared protected WebDriver driver def setup() { this.driver = DriverFactory.createDriver() driver.get("www.anywebservice.ru") } void cleanup() { driver.quit() } } Здесь мы видим базовый класс спецификации страницы. В начале класса мы видим импорт необходимых классов. Далее представлена аннотация shared, позволяющая классам-наследникам получить доступ к веб-драйверу. В блоке setup() мы видим код инициализации веб-драйвера и открытия веб-страницы. В блоке cleanup() – код завершения работы веб-драйвера. Далее перейдем к обзору спецификации страницы авторизации пользователя. import pages.LoginPage
import spock.lang.Issue class LoginPageTest extends PagesBaseSpec { @Issue("QAA-1") def "QAA-1: Authorization with correct login and password"() { given: "Login page" def loginPage = new LoginPage(driver) and: "Correct login and password" def adminLogin = "adminLogin" def adminPassword = "adminPassword" when: "Log in with correct login and password" loginPage.login(adminLogin, adminPassword) then: "Authorized and moved to main page" driver.currentUrl == "www.anywebservice.ru/main" } } Спецификация страницы авторизации наследуется от базовой спецификации страниц. Аннотация Issue задает идентификатор теста во внешней системе трекинга (например, Jira). В следующей строке мы видим название теста, которое по соглашению задается строковыми литералами, что позволяет использовать любые символы в названии теста (в том числе и русскоязычные). В блоке given происходит инициализация page object класса страницы авторизации, а также получение корректных логина и пароля для авторизации в системе. В блоке when выполняется действие по авторизации. В блоке then – проверка ожидаемого действия, а именно – успешная авторизация и переадресация на главную страницу системы. На примере данной спецификации мы видим наиболее значимый плюс использования парадигмы BDD в spock – спецификация системы одновременно является и ее документацией. Каждый тест описывает определенное поведение, каждый шаг в тесте имеет свое описание, понятное не только разработчикам, но и заказчикам. Описание блоков может быть представлено не только в исходном коде теста, но и в диагностических сообщениях или отчетах о работе теста. В фреймворке предусмотрена возможность передавать различные логины и пароли для тестирования (параметризировать тест). Data Driven Testing в Spock Framework Data Driven Testing = table-driven testing = parameterized testing
Для тестирования сценария с несколькими параметрами можно использовать различные варианты их передачи. Таблицы данных (Data Tables) Рассмотрим несколько примеров из официальной документации фреймворка. class MathSpec extends Specification {
def "maximum of two numbers"() { expect: Math.max(a, b) == c where: a | b | c 1 | 3 | 3 7 | 4 | 7 0 | 0 | 0 } } Каждая строка в таблице – отдельная итерация теста. Также таблица может быть представлена и одним столбцом. where:
a | _ 1 | _ 7 | _ 0 | _ _ — объект-заглушка класса спецификации. Для лучшего визуального восприятия параметров можно переписать пример выше в следующем виде: def "maximum of two numbers"() {
expect: Math.max(a, b) == c where: a | b || c 1 | 3 || 3 7 | 4 || 7 0 | 0 || 0 } Потоки данных (Data pipes) В некоторых случаях использование таблицы параметров будет выглядеть очень громоздко. В таких случаях можно применять следующий вид передачи параметров: ...
where: a << [1, 7, 0] b << [3, 4, 0] c << [3, 7, 0] Здесь левый сдвиг << – перегруженный groovy оператор, который теперь выполняет роль добавления элементов в список. Для каждой итерации теста будут запрашиваться следующие данные из списка для каждой переменной: 1 итерация: a=1, b=3, c=3; 2 итерация: a=7, b=4, c=7; 3 итерация: a=0, b=0, c=0. Причем входные данные могут не только передаваться явно, но и запрашиваться при необходимости из различных источников. Например, из базы данных: @Shared sql = Sql.newInstance("jdbc:h2:mem:", "org.h2.Driver")
def "maximum of two numbers"() { expect: Math.max(a, b) == c where: [a, b, c] << sql.rows("select a, b, c from maxdata") } Переменная как данные (Data Variable Assignment) ...
where: a = 3 b = Math.random() * 100 c = a > b ? a : b Здесь мы видим динамически вычисляемую переменную c в тестовых данных. Комбинация различных видов передачи параметров ...
where: a | _ 3 | _ 7 | _ 0 | _ b << [5, 0, 0] c = a > b ? a : b Никто не запрещает вам применять сразу несколько видов передачи, если это необходимо. Пример реализации параметризованного теста на Spock Framework @Issue("QAA-1-parametrized")
def "QAA-1-parametrized: Authorization with correct login and password"() { given: "Login page" def loginPage = new LoginPage(driver) when: "Log in with correct login and password" loginPage.login(login, password) then: "Authorized and moved to main page" driver.currentUrl =="www.anywebservice.ru/main" where: "Check for different logins and passwords" login | password "adminLogin" | "adminPassword" "moderatorLogin" | "moderatorPassword" "userLogin" | "userPassword" } Здесь мы видим уже знакомый нам блок where, в котором заданы ключи параметров (логинов и паролей), которые хранятся в файле конфигурации. Ввиду особенностей используемой реализации спецификации, цикл настройки веб-драйвера и его закрытие (а значит и закрытие браузера) будет выполняться для каждого параметра, что негативно скажется на времени выполнения теста. Предлагаем доработать спецификацию и улучшить время работы теста. Пример реализации параметризованного теста с доработанной спецификацией До доработки abstract class PagesBaseSpec extends Specification {
@Shared protected WebDriver driver def setup() { this.driver = DriverFactory.createDriver() driver.get("www.anywebservice.ru") } void cleanup() { driver.quit() } } После доработки import helpers.DriverFactory
import org.openqa.selenium.WebDriver import spock.lang.Shared import spock.lang.Specification abstract class PagesNoRestartBaseSpec extends Specification { @Shared protected WebDriver driver def setupSpec() { this.driver = DriverFactory.createDriver() } def setup() { this.driver.get("www.anywebservice.ru") } def cleanup() { this.driver.get("www.anywebservice.ru/logout") this.driver.manage().deleteAllCookies(); } void cleanupSpec() { this.driver.quit() } } В обновленной спецификации мы видим, что процедура создания веб-драйвера будет выполняться только при настройке класса спецификации, а закрытие браузера – только после завершения работы тестов из спецификации. В методе setup() мы видим тот же код получения веб-адреса сервиса и его открытие в браузере, а в методе cleanup() – переход по адресу www.anywebservice.ru/logout для завершения работы с сервисом у текущего пользователя и удаления файлов куки (для тестирования текущего веб-сервиса данной процедуры достаточно, чтобы имитировать «уникальный» запуск). Код самого теста не изменился. В итоге с помощью нехитрых доработок мы получили минимум двукратное уменьшение времени работы автотеста, по сравнению с первичной реализацией. Сравнение тестов на testNG, pytest, pytest-bdd Для начала мы рассмотрим реализацию теста на тестовом фреймворке testNG на языке программирования Java, который также, как и Spock Framework, вдохновлен фреймворком jUnit и поддерживает data-driven testing. package javaTests;
import org.testng.Assert; import org.testng.annotations.*; import pages.LoginPage; public class LoginPageTest extends BaseTest { @BeforeClass public final void setup() { createDriver(); driver.get("www.anywebservice.ru"); } @DataProvider(name = "userParameters") public final Object[][] getUserData(){ return new Object[][] { {"adminLogin", "adminPassword"}, {"moderatorLogin", "moderatorPassword"}, {"userLogin", "userPassword"} }; } @Test(description = "QAA-1-1: Authorization with correct login and password", dataProvider = "userParameters") public final void authorizationWithCorrectLoginAndPassword(String login, String password){ //Login page LoginPage loginPage = new LoginPage(driver); //Log in with correct login and password loginPage.login(login, password); //Authorized and moved to main page Assert.assertEquals("www.anywebservice.ru/main", driver.getCurrentUrl()); } @AfterMethod public final void cleanup() { driver.get("www.anywebservice.ru/logout"); driver.manage().deleteAllCookies(); } @AfterClass public final void tearDown() { driver.quit(); } } Здесь мы можем видеть тестовый класс со всеми необходимыми setup(), cleanup() методами, а также параметризацию теста в виде дополнительного метода getUserData() с аннотацией @DataProvider, что выглядит несколько громоздко, после того, что мы рассмотрели в тесте с использованием Spock Framework. Также для понимания того, что происходит в тесте, были оставлены комментарии, аналогичные описанию шагов. Стоит отметить, что в testNG, в отличие от Spock Framework, реализована поддержка параллельного выполнения теста. Далее перейдем к тесту с использованием тестового фреймворка pytest на языке программирования Python. import pytest
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from PageObjects.LoginPage import LoginPage class TestLogin(object): @pytest.mark.parametrize("login,password", [ pytest.param(("adminLogin", "adminPassword"), id='admin'), pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'), pytest.param(("userLogin", "userPassword"), id='user') ]) def test_authorization_with_correct_login_and_password(self, login, password, driver, test_cleanup): # Login page login_page = LoginPage(driver) # Log in with correct login and password login_page.login(login, password) # Authorized and moved to main page assert expected_conditions.url_to_be("www.anywebservice.ru/main") @pytest.fixture() def test_cleanup(self, driver): yield "test" driver.get("www.anywebservice.ru/logout") driver.delete_all_cookies() Здесь мы также видим поддержку data-driven testing в виде отдельной конструкции, схожей с @DataProvider в testNG. Метод настройки веб-драйвера «спрятан» в фикстуре driver. Благодаря динамической типизации и фикстурам pytest, код этого теста выглядит чище, чем на Java. Далее перейдем к обзору кода теста с использованием плагина pytest-bdd, который позволяет писать тесты в виде feature файлов Gherkin (чистый BDD-подход). login.feature Feature: Login page
A authorization Scenario: Authorizations with different users Given Login page When Log in with correct login and password Then Authorized and moved to main page test_login.py import pytest
from pytest_bdd import scenario, given, when, then from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from PageObjects.LoginPage import LoginPage @pytest.mark.parametrize("login,password", [ pytest.param(("adminLogin", "adminPassword"), id='admin'), pytest.param(("moderatorLogin", "moderatorPassword"), id='moderator'), pytest.param(("userLogin", "userPassword"), id='user') ]) @scenario('login.feature', 'Authorizations with different users') def test_login(login, password): pass @given('Login page') def login_page(driver): return LoginPage(driver) @when('Log in with correct login and password') def login_with_correct_login_and_password(login_page, login, password): login_page_object = login_page login_page_object.login(login, password) @then('Authorized and moved to main page') def authorized_and_moved_to_main_page(driver, login): assert expected_conditions.url_to_be("www.anywebservice.ru/main") Из плюсов можно выделить то, что это все еще фреймворк pytest, который имеет множество плагинов для различных ситуаций, в том числе и для параллельного запуска тестов. Из минусов – сам чистый BDD-подход, который будет постоянно ограничивать разработчика своими особенностями. Spock Framework дает возможность писать более лаконичный и простой в оформлении код, по сравнению со связкой PyTest + pytest-bdd. Заключение В этой статье мы рассмотрели возможность упрощения работы с BDD с помощью фреймворка Spock. Подводя итоги, кратко выделим основные, на наш взгляд, плюсы и минусы Spock по сравнению с некоторыми другими распространенными тестовыми фреймворками. Плюсы:
Минусы:
В конечном итоге, инструмент Spock Framework, несомненно, заслуживает внимания, поскольку подходит для решения сложной распространенной задачи автоматизации бизнес-сценариев для программных продуктов с высокой предметной и организационной сложностью. Что еще можно почитать:
=========== Источник: habr.com =========== Похожие новости:
Блог компании SimbirSoft ), #_testirovanie_itsistem ( Тестирование IT-систем ), #_programmirovanie ( Программирование ), #_testirovanie_vebservisov ( Тестирование веб-сервисов ), #_upravlenie_razrabotkoj ( Управление разработкой ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 23-Ноя 01:31
Часовой пояс: UTC + 5