Cucumber плюс Spring и Selenium или жирное BDD

Заметка с громким названием на достаточно спорную тему - BDD. Многие используют Behavior-Driven Development у себя на проектах, многие его ругают. Но, я уверен, есть люди, которые не видели и не пробовали, как это работает. О том, что такое BDD, в чем его основной смысл, вы можете посмотреть здесь.

Дальше я покажу, как настроить проект, показанный на видео по ссылке выше, с использованием Cucumber, Spring Framework и Selenium. Традиционно пример будет реализован на Java.

Итак начнем. Создаем простой Maven проект и добавляем зависимости в pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>CucumberDemo</groupId>
    <artifactId>CucumberDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <cucumber-core.version>1.1.8</cucumber-core.version>
        <cucumber-html.version>0.2.3</cucumber-html.version>
        <cucumber-java.version>1.1.8</cucumber-java.version>
        <cucumber-junit.version>1.1.8</cucumber-junit.version>
        <cucumber-jvm-deps.version>1.0.3</cucumber-jvm-deps.version>
        <cucumber-spring.version>1.1.8</cucumber-spring.version>
        <gherkin.version>2.12.2</gherkin.version>
        <hamcrest-all.version>1.3</hamcrest-all.version>
        <junit.version>4.11</junit.version>
        <selenium-server.version>2.46.0</selenium-server.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-core</artifactId>
            <version>${cucumber-core.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-html</artifactId>
            <version>${cucumber-html.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>${cucumber-java.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>${cucumber-junit.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-jvm-deps</artifactId>
            <version>${cucumber-jvm-deps.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>${cucumber-spring.version}</version>
        </dependency>
        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>gherkin</artifactId>
            <version>${gherkin.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-all</artifactId>
            <version>${hamcrest-all.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-server</artifactId>
            <version>${selenium-server.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.1.5.RELEASE</version>
        </dependency>
    </dependencies>
</project>

Далее нам нужно создать класс CucumberRunner, который будет запускать наши сценарии. Сейчас вы удивитесь краткости настроек:

@RunWith(Cucumber.class)
@CucumberOptions(tags = {},format = {"pretty", "json:target/cucumber.json","html:target/cucumber.html"}, features = {"src/main/java/com/features/"})
public class CucumberRunner {

}

Здесь мы указали формат вывода результатов, формат отчета и путь к папке, где лежат наши feature файлы.

Теперь создадим непосредственно сам feature файл pc.feature:

Feature: PC Feature

  @1
  Scenario: Verify that user can open PC page
    Given I open pn.com.ua
    When I select category "Компьютеры"
    Then I should see page url "http://pn.com.ua/computers/"

  @2
  Scenario: Verify that user can open PC page
    Given I open pn.com.ua
    When I select category "Компьютеры"
    Then verify that has sub-column:
    |name|
    |Ноутбуки, планшеты|

Теги @1 и @2 - это аннотации, с помощью которых мы можем фильтровать сценарии при запуске, указав параметр в CucumberRunner, к примеру tags{"@2"}. Ну вот, с настройкой Cucumber мы справились, теперь приступим к настройке Spring, который будет управлять зависимостями в нашем фреймворке. Создаем в папке src/java/resources файл cucumber.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:annotation-config/>

    <context:component-scan base-package="com.cucumber"/>

    <import resource="classpath*:/applicationContext.xml"/>
    <bean id="driver" class="org.openqa.selenium.firefox.FirefoxDriver" destroy-method="quit"/>
</beans>

Теперь приступим к ComponentObject. Создаем класс Page, от которого будем наследовать все наши классы-компоненты.

public class Page {

    @Autowired
    WebDriver driver;

    @PostConstruct
    public void setUp() {
        driver.manage().window().maximize();
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    }

    protected void open(String url) {
        driver.get(url);
    }


    protected WebElement $(String xpath, String... args) {
        return driver.findElement(By.xpath(String.format(xpath, args)));
    }


    protected List<WebElement> $$(By by) {
        return driver.findElements(by);
    }

    protected WebElement $(By by) {
        return driver.findElement(by);
    }
}

Создаем класс-компонент Home:

@Component
public class Home extends Page{

    private static final String URl = "http://pn.com.ua/";

    public void open(){
        open(URl);
    }

    public WebElement getCategoty(String name){
        return $("//a[contains(.,'%s')]",name);
    }
}

Аннотация @Component говорит Spring о том, что нужно создать инстанс этого класса.

Создаем класс HomeSteps:

public class HomeSteps {

    @Autowired
    Home home;

    @Given("^I open pn\\.com\\.ua$")
    public void shouldOpenMainPage() throws Throwable {
        home.open();
    }

    @When("^I select category \"(.*?)\"$")
    public void shoulSelectCategory(String cat) throws Throwable {
        home.getCategoty(cat).click();
    }

}

Вот она магия Spring - не нужно никаких конструкторов и прочей лишней чепухи, ставим аннотацию @Autowired и все. На этом, собственно, вся настройка заканчивается. Остается создать оставшиеся классы-компоненты, реализовать шаги и запустить тесты. В конце получается красивенький HTML - отчет о результатах прохождения тестов, смотреть его в папке \target\cucumber.html.

Полный код примера вы можете посмотреть в репозитории bitbucket.

Качайте, пробуйте, подписывайтесь на email рассылку, чтобы получать свежие заметки первыми. Спасибо

Частные консультации и обучение