вторник, 14 августа 2012 г.

Перевод статьи по использованию Selenium и JBehave

Оригинал

Driving Web Behaviour

Selenium является мощным инструментом с открытым исходным кодом для автоматизированного тестирования web-приложений. С версии 2.x, он предлагает на выбор два API: API Selenium (обратная совместимость с 1.x) и новый WebDriver API. JBehave поддерживает оба. Когда мы говорим о Selenium, мы обычно подразумеваем сам фреймворк, а если нужно различать API, мы специально обратимся к API и Selenium WebDriver API.
Давайте посмотрим, как мы можем легко использовать Selenium для запуска "веб-историй". Целью создания DSL является представление бизнес-функциональности домена в то же время абстрагируясь от деталей реализации, в этом случае доступ к веб-слою осуществляется с помощью специального инструмента тестирования, такого как Selenium. То же самое DSL должно иметь возможность повторного использования с другими инструментами тестирования.
Как обычно, примеры говорят громче, чем слова:

Scenario: User searches for a single step
Given user is on Home page
When user opens Find Steps page
Then Find Steps page is shown
When user searches for "Given a threshold of 10.0"
Then search returns: "Given a threshold of $threshold"
When user views with methods
Then search returns: "Given a threshold of $threshold,
    [org.jbehave.web.examples.trader.steps.TraderSteps.aThreshold(double)]"
And steps instances include: "TraderSteps,StockExchangeSteps"
TraderWebSteps класс, как и любой другой класс JBehave с шагами, выглядит следующим образом:
public class TraderWebSteps {
    private final Pages pages;
    public TraderWebSteps(Pages pages) {
        this.pages = pages;
    }
    @Given("user is on Home page")
    public void userIsOnHomePage(){       
        pages.home().open();       
    }
    @When("user opens Find Steps page")
    public void userClicksOnFindSteps(){       
        pages.findSteps().open();
    }
    @Then("Find Steps page is shown")
    public void findStepsPageIsShown(){
        pages.findSteps().pageIsShown();
    }
     
    @When("user searches for \"$step\"")
    public void userSearchesForSteps(String step){       
        pages.findSteps().find(step);
    }
    @Then("search returns: \"$stepsOrMethods\"")
    public void stepsFound(List<String> stepsOrMethods){  
        pages.findSteps().found(stepsOrMethods);
    }
    @Then("steps instances include: \"$names\"")
    public void stepsInstancesFound(List<String> names){  
        pages.findSteps().found(names);
    }
}
Новым здесь является то, что мы используем Page Object для абстрагирования от поведения Selenium за страницами, которые определяют взаимодействие с пользователем. Page Object, это то место где происходит магия Selenium. Отметим, что использование страниц до сих пор не позволило нам взять на себя обязательство использования конкретного API, как Selenium API или WebDriver API.

Настройка JBehave для использования Selenium

Как всегда, мы настроим JBehave несколькими различными способами. Один из способов, это иметь встраиваемый запускаемый класс, то есть JUnit-запускаемый класс TraderWebStories. Здесь в игру вступает выбор API.

Использование Selenium API

public class TraderWebStories extends JUnitStories {
   
    private Selenium selenium = SeleniumConfiguration.defaultSelenium();
    private ConditionRunner conditionRunner = SeleniumConfiguration.defaultConditionRunner(selenium);
    private Pages pages = new Pages(selenium, conditionRunner);
    private SeleniumSteps lifecycleSteps = new PerStorySeleniumSteps(selenium);
    private SeleniumContext seleniumContext = new SeleniumContext();
    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        return new SeleniumConfiguration()
            .useSelenium(selenium)
            .useSeleniumContext(seleniumContext)
            .useStepMonitor(new SeleniumStepMonitor(selenium, seleniumContext, new SilentStepMonitor()))
            .useStoryLoader(new LoadFromClasspath(embeddableClass))
            .useStoryReporterBuilder(new StoryReporterBuilder()
                .withCodeLocation(codeLocationFromClass(embeddableClass))
                .withDefaultFormats()
                .withFormats(CONSOLE, TXT, HTML, XML));
    }
    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(),
                new TraderWebSteps(pages),
                lifecycleSteps,
                new SeleniumScreenshotOnFailure(selenium));
    }
     
    @Override
    protected List<String> storyPaths() {
        return new StoryFinder()
                .findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
    }
}

Использование API WebDriver

public class TraderWebStories extends JUnitStories {
   
    private WebDriverProvider driverProvider = new PropertyWebDriverProvider();
    private WebDriverSteps lifecycleSteps = new PerStoriesWebDriverSteps(driverProvider); // or PerStoryWebDriverSteps(driverProvider)
    private Pages pages = new Pages(driverProvider);
    private SeleniumContext context = new SeleniumContext();
    private ContextView contextView = new LocalFrameContextView().sized(500, 100);
     
    public TraderWebStories() {
        // If configuring lifecycle per-stories, you need to ensure that you a same-thread executor
        if ( lifecycleSteps instanceof PerStoriesWebDriverSteps ){
            configuredEmbedder().useExecutorService(MoreExecutors.sameThreadExecutor());
        }
    }
    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        return new SeleniumConfiguration()
                .useSeleniumContext(context)
                .useWebDriverProvider(driverProvider)
                .useStepMonitor(new SeleniumStepMonitor(contextView, context, new SilentStepMonitor()))
                .useStoryLoader(new LoadFromClasspath(embeddableClass))
                .useStoryReporterBuilder(new StoryReporterBuilder()
                    .withCodeLocation(codeLocationFromClass(embeddableClass))
                    .withDefaultFormats()
                    .withFormats(CONSOLE, TXT, HTML, XML));
    }
    @Override
    public InjectableStepsFactory stepsFactory() {
        Configuration configuration = configuration();
        return new InstanceStepsFactory(configuration,
                new TraderWebSteps(pages),
                lifecycleSteps,
                new WebDriverScreenshotOnFailure(driverProvider, configuration.storyReporterBuilder()));
    }
    @Override
    protected List<String> storyPaths() {
        return new StoryFinder()
                .findPaths(codeLocationFromClass(this.getClass()).getFile(), asList("**/*.story"), null);
    }
    // This Embedder is used by Maven or Ant and it will override anything set in the constructor
    public static class SameThreadEmbedder extends Embedder {
         
        public SameThreadEmbedder() {
            useExecutorService(MoreExecutors.sameThreadExecutor());
        }
    }
}

Жизненный цикл шагов

JBehave интеграции с Selenium и WebDriver API, направлена ​​на выполнение общих задач. Среди них одна из самых распространенных является управление жизненным циклом, например, запуском и остановкой браузера.
JBehave предусматривает три типа управления жизненным циклом:
  1. Через истории: через PerStoriesSeleniumSteps или PerStoriesWebDriverSteps .
  2. Через историю: через PerStorySeleniumSteps или PerStoryWebDriverSteps .
  3. Через сценарий: через PerScenarioSeleniumSteps или PerScenarioWebDriverSteps .
Если вы используете контроль жизненного цикла через истории, вы должны убедиться, что вы используете нужный испольнитель.  trader-webdriver пример показывает, как настроить использование одного и того же потока исполнителя.

Запуск Selenium-тестов в автоматическом режиме

ПРИМЕЧАНИЕ: Для запуска Selenium-тестов  в автоматическом режиме, вам нужно запустить два сервера, например Jetty, и Selenium Server (только для Selenium API, для WebDriver API не нужно). См. trader runner selenium example и trader runner webdriver example, отличным способом для того чтобы сделать это будет Maven.