Автоматизация тестирования помогает постоянно контролировать качество IT-продукта, а также снижать затраты в долгосрочной перспективе. В автоматизации существуют различные подходы, например, Behavior Driven Development (BDD), разработка через поведение.
С этим подходом связаны инструменты cucumber, robot framework, behave и другие, в которых разделены сценарии выполнения и реализация каждой конструкции. Такое разделение помогает составить удобочитаемые сценарии, но требует значительных затрат времени и поэтому может быть непрактичным при написании реализации.
Рассмотрим, как можно упростить работу с BDD, используя подходящие инструменты – например, фреймворк Spock, который сочетает в себе красоту, удобство принципов BDD и особенности jUnit.
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 является объектно-ориентированным языком программирования, разработанным для платформы Java как дополнение с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java-кодом и библиотеками. Язык может использоваться в любом Java-проекте или как скриптовый язык.
К особенностям groovy относятся: как статическая, так и динамическая типизация; встроенный синтаксис для списков, массивов и регулярных выражений; перегрузка операций. При этом замыкания в Groovy появились задолго до Java.
Groovy хорошо подходит для быстрой разработки тестов, когда есть возможность применять «синтаксический сахар», схожий с python, и не задумываться о типизации объектов.
Одна из ключевых особенностей фреймворка – у разработчика есть возможность писать спецификации с ожидаемыми характеристиками системы с использованием принципов BDD подхода. Этот подход позволяет составлять бизнес-ориентированные функциональные тесты для программных продуктов с высокой предметной и организационной сложностью.
Спецификация представляет собой класс groovy, расширяющий spock.lang.Specification
Спецификация может содержать различные вспомогательные поля, которые инициируются для каждого класса спецификации.
С помощью аннотации @Shared можно дать доступ к полю классам-наследникам спецификации.
Методы настройки класса спецификации:
В следующей таблице рассмотрим, у каких ключевых слов и методов Spock framework есть аналоги в JUnit.
В Spock Framework каждая фаза теста выделена в отдельный блок кода (см. пример в документации).
Блок кода начинается с лейбла и завершается началом следующего блока кода или окончанием теста.
Блок given отвечает за настройку начальных условий теста.
Блоки when, then всегда используются вместе. В блоке when – стимулятор, раздражитель системы, а в блоке then – ответная реакция системы.
В тех случаях, когда есть возможность сократить конструкцию when-then до одного выражения, можно использовать один блок expect. Далее будут использованы примеры из официальной документации Spock framework:
или одно выражение
Блок cleanup применяют для освобождения ресурсов перед следующей итерацией теста.
Блок where применяют для передачи данных для тестирования (Data Driven Testing).
Виды передачи входных данных будут рассмотрены далее.
Далее рассмотрим подходы к реализации тестирования веб-страницы авторизации пользователя в системе с использованием selenium.
Здесь мы видим базовый класс спецификации страницы. В начале класса мы видим импорт необходимых классов. Далее представлена аннотация shared, позволяющая классам-наследникам получить доступ к веб-драйверу. В блоке setup() мы видим код инициализации веб-драйвера и открытия веб-страницы. В блоке cleanup() – код завершения работы веб-драйвера.
Далее перейдем к обзору спецификации страницы авторизации пользователя.
Спецификация страницы авторизации наследуется от базовой спецификации страниц. Аннотация Issue задает идентификатор теста во внешней системе трекинга (например, Jira). В следующей строке мы видим название теста, которое по соглашению задается строковыми литералами, что позволяет использовать любые символы в названии теста (в том числе и русскоязычные). В блоке given происходит инициализация page object класса страницы авторизации, а также получение корректных логина и пароля для авторизации в системе. В блоке when выполняется действие по авторизации. В блоке then – проверка ожидаемого действия, а именно – успешная авторизация и переадресация на главную страницу системы.
На примере данной спецификации мы видим наиболее значимый плюс использования парадигмы BDD в spock – спецификация системы одновременно является и ее документацией. Каждый тест описывает определенное поведение, каждый шаг в тесте имеет свое описание, понятное не только разработчикам, но и заказчикам. Описание блоков может быть представлено не только в исходном коде теста, но и в диагностических сообщениях или отчетах о работе теста.
В фреймворке предусмотрена возможность передавать различные логины и пароли для тестирования (параметризировать тест).
Для тестирования сценария с несколькими параметрами можно использовать различные варианты их передачи.
Рассмотрим несколько примеров из официальной документации фреймворка.
Каждая строка в таблице – отдельная итерация теста. Также таблица может быть представлена и одним столбцом.
_ — объект-заглушка класса спецификации.
Для лучшего визуального восприятия параметров можно переписать пример выше в следующем виде:
Теперь мы видим, что a, b – входные параметры, а c – ожидаемое значение.
В некоторых случаях использование таблицы параметров будет выглядеть очень громоздко. В таких случаях можно применять следующий вид передачи параметров:
Здесь левый сдвиг << – перегруженный groovy оператор, который теперь выполняет роль добавления элементов в список.
Для каждой итерации теста будут запрашиваться следующие данные из списка для каждой переменной:
1 итерация: a=1, b=3, c=3;
2 итерация: a=7, b=4, c=7;
3 итерация: a=0, b=0, c=0.
Причем входные данные могут не только передаваться явно, но и запрашиваться при необходимости из различных источников. Например, из базы данных:
Здесь мы видим динамически вычисляемую переменную c в тестовых данных.
Никто не запрещает вам применять сразу несколько видов передачи, если это необходимо.
Здесь мы видим уже знакомый нам блок where, в котором заданы ключи параметров (логинов и паролей), которые хранятся в файле конфигурации.
Ввиду особенностей используемой реализации спецификации, цикл настройки веб-драйвера и его закрытие (а значит и закрытие браузера) будет выполняться для каждого параметра, что негативно скажется на времени выполнения теста. Предлагаем доработать спецификацию и улучшить время работы теста.
В обновленной спецификации мы видим, что процедура создания веб-драйвера будет выполняться только при настройке класса спецификации, а закрытие браузера – только после завершения работы тестов из спецификации. В методе setup() мы видим тот же код получения веб-адреса сервиса и его открытие в браузере, а в методе cleanup() – переход по адресу www.anywebservice.ru/logout для завершения работы с сервисом у текущего пользователя и удаления файлов куки (для тестирования текущего веб-сервиса данной процедуры достаточно, чтобы имитировать «уникальный» запуск). Код самого теста не изменился.
В итоге с помощью нехитрых доработок мы получили минимум двукратное уменьшение времени работы автотеста, по сравнению с первичной реализацией.
Для начала мы рассмотрим реализацию теста на тестовом фреймворке testNG на языке программирования Java, который также, как и Spock Framework, вдохновлен фреймворком jUnit и поддерживает data-driven testing.
Здесь мы можем видеть тестовый класс со всеми необходимыми setup(), cleanup() методами, а также параметризацию теста в виде дополнительного метода getUserData() с аннотацией @DataProvider, что выглядит несколько громоздко, после того, что мы рассмотрели в тесте с использованием Spock Framework. Также для понимания того, что происходит в тесте, были оставлены комментарии, аналогичные описанию шагов.
Стоит отметить, что в testNG, в отличие от Spock Framework, реализована поддержка параллельного выполнения теста.
Далее перейдем к тесту с использованием тестового фреймворка pytest на языке программирования Python.
Здесь мы также видим поддержку data-driven testing в виде отдельной конструкции, схожей с @DataProvider в testNG. Метод настройки веб-драйвера «спрятан» в фикстуре driver. Благодаря динамической типизации и фикстурам pytest, код этого теста выглядит чище, чем на Java.
Далее перейдем к обзору кода теста с использованием плагина pytest-bdd, который позволяет писать тесты в виде feature файлов Gherkin (чистый BDD-подход).
login.feature
test_login.py
Из плюсов можно выделить то, что это все еще фреймворк pytest, который имеет множество плагинов для различных ситуаций, в том числе и для параллельного запуска тестов. Из минусов – сам чистый BDD-подход, который будет постоянно ограничивать разработчика своими особенностями. Spock Framework дает возможность писать более лаконичный и простой в оформлении код, по сравнению со связкой PyTest + pytest-bdd.
В этой статье мы рассмотрели возможность упрощения работы с BDD с помощью фреймворка Spock. Подводя итоги, кратко выделим основные, на наш взгляд, плюсы и минусы Spock по сравнению с некоторыми другими распространенными тестовыми фреймворками.
Плюсы:
Минусы:
В конечном итоге, инструмент Spock Framework, несомненно, заслуживает внимания, поскольку подходит для решения сложной распространенной задачи автоматизации бизнес-сценариев для программных продуктов с высокой предметной и организационной сложностью.
Что еще можно почитать:
С этим подходом связаны инструменты 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, несомненно, заслуживает внимания, поскольку подходит для решения сложной распространенной задачи автоматизации бизнес-сценариев для программных продуктов с высокой предметной и организационной сложностью.
Что еще можно почитать: