вторник, 22 мая 2012 г.

BDD и Selenium

В данной статье пойдет речь о BDD и его применении к тестированию веб-приложений.
Ну и естественно не обойдется без нашего любимого Selenium.

ЧАСТЬ ПЕРВАЯ

Что такое BDD ? Если в двух словах BDD это подход к тестированию и разработке с использованием пользовательских сценариев, как основы для тестов. Данный подход призван сократить расстояние между аналитиками, разработчиками и тестировщиками. 

Подробная информация размещена тут и тут

Мы будем рассматривать библиотеку JBehave.

Как выглядит сценарий тестирования на JBehave ? Это файл с расширением .story и примерно следующего содержания:
Scenario:  trader is not alerted below threshold
Given a stock of symbol STK1 and a threshold of 10.0
When the stock is traded at 5.0
Then the alert status should be OFF
Scenario:  trader is alerted above threshold
Given a stock of symbol STK1 and a threshold of 10.0
When the stock is traded at 11.0
Then the alert status should be ON

Судя по виду не сложно. Сценарии в таком виде позволяют описывать  use-кейсы вполне человеческим языком.

Все предложения в файле должны начинаться со слов Given Then и When.

Предположим мы решили написать для примера небольшую историю для проверки открытия сайта и проверки его заголовка.

Напишем что-то вроде 

Given site http://www.google.com
When title is Google
Then close

и сохраним как behave_example.story

История использования у нас уже есть, теперь перейдем к реализации кода, который скажет браузеру что с этой историей делать.

ЧАСТЬ ВТОРАЯ

Создадим в Eclipse maven-проект. Назовем его JBehaveProj. (не будем цепляться к названию, это только для примера).

В pom.xml в секции dependency запишем следующее

<dependencies>
      <dependency>
          <groupId>org.jbehave</groupId>
          <artifactId>jbehave-core</artifactId>
      </dependency>
      <dependency>
          <groupId>org.seleniumhq.selenium</groupId>
          <artifactId>selenium-java</artifactId>
      </dependency>
  </dependencies>
  <dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.jbehave</groupId>
              <artifactId>jbehave-core</artifactId>
              <version>3.6.2</version>
          </dependency>
          <dependency>
              <groupId>org.seleniumhq.selenium</groupId>
              <artifactId>selenium-java</artifactId>
              <version>2.21.0</version>
          </dependency>
      </dependencies>
  </dependencyManagement>

Теперь maven подгрузит нам две библиотеки jbehave-core и selenium-java. Обе они нам понадобятся.

Теперь когда наш проект настроен напишем код.

Создадим класс BehaveTests , этот класс будет содержать имплементацию того что написано в файле .story.

public class BehaveTests {

    FirefoxDriver driver = new FirefoxDriver();

@Given("site $url")   
public void open(String url){
driver.get(url);
}

@When("title is $title")
public void find(String title ){
    if (driver.getTitle().equals(title)){
        System.out.println("Совпало");
       
    }
    else{
        System.out.println("Не совпало");
    }
}

@Then("close")
public void close(){
driver.close();   
}

Надеюсь все что тут написано не вызовет вопросов )))))))) Мы используем аннотации взятые из библиотеки jbehave и говорим с какими фразами из файла .story они должны совпадать.
Слова со знаком $ это переменные, имя переменной должно совпадать с именем параметра в аннотированном методе.

Теперь самое интересное:
Каким способом это все запустить ???????????????????

Для этого напишем еще один класс BehaveExample (название не случайное ).

public class BehaveExample extends JUnitStory  {

        @Override
        public Configuration configuration() {
            return new MostUsefulConfiguration()
                // Ишем истории в Classpath
                .useStoryLoader(new LoadFromClasspath(this.getClass()))
                // Вывод в консоль и текстовый файл
                .useStoryReporterBuilder(new StoryReporterBuilder().withDefaultFormats().withFormats(Format.CONSOLE, Format.TXT));
        }
   
        // Тут опишем наши классы с описанием шагов
        @Override
        public InjectableStepsFactory stepsFactory() {      
                     return new InstanceStepsFactory(configuration(), new BehaveTests());
        }
   
}

для запуска уже почти все готово. Теперь положим наш файл истории рядом с файлом BehaveExample.java. Запустим BehaveExample.java как JUnit тест.

Вуаля )))) Открылся браузер, зашел на гугл, проверил заголовок и закрылся, как раз так как мы и описывали !!!!!!!





вторник, 15 мая 2012 г.

Maven configuration for Java 6

Многие начинающие пользователи maven сталкивались с проблемой что проект в ide собирается, а при сборке из командной строки начинает ругаться на версию Java.
Чаще всего такое поведение вызвано использованием в вашем коде штучек из Java 5,6,7.
Для решения этой проблемы в pom.xml нужно добавить следующие записи:
<properties>
            <java.version>1.5</java.version>
</properties>



И добавить следующие строки в секцию <build>

<plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>

после добавления этих записей все начнет собираться из командной строки без ошибок.

четверг, 10 мая 2012 г.

CSS и XPath


В таблице ниже вы можете увидеть соответствие между xpath запросом и его аналогом в css.
 
 
Таблица преобразования CSS селекторов в запросы XPath.
CSS селектор XPath запрос Описание
* //* Любой элемент
E //E Элемент с тэгом E (типа E)
E[foo] //E[@foo] Элемент E, у которого есть атрибут foo
E[foo="bar"] //E[@foo="bar"] Элемент E, у которого значение атрибута foo равно bar
E[foo~="bar"] //E[contains( concat(" ", @foo, " "), concat(" ", "bar", " ") )] Элемент E, у которого значение атрибута foo представляет собой список значений, разделенных пробелами, и одно из них равно bar
E[foo^="bar"] //E[starts-with(@foo, "bar")] Элемент E, у которого значение атрибута foo начинается с bar
E[foo$="bar"] //E[substring(@foo, string-length(@foo) - string-length("bar") + 1) = "bar"] Элемент E, у которого значение атрибута foo заканчивается bar
E[foo*="bar"] //E[contains(@foo, "bar")] Элемент E, у которого значение атрибута foo содержит bar
E[foo|="bar"] //E[@foo="bar" or starts-with(@foo, concat("bar", "-")] Элемент E, у которого значение атрибута foo представляет собой разделенный дефисами список значений, который начинается с bar
E:nth-child(a*n+b) a < 0: //*[position() <= b][name() = "E"]
a == 0: //*[position() = b][name() = "E"]
a > 0: //*[(position() + b) mod a = 0][name() = "E"]
Элемент E, который является a*n+b ребенком своего родителя
E:nth-last-child(a*n+b) a < 0: //*[position() > last() — b][name() = "E"]
a == 0: //*[last() - b][name() = "E"]
a > 0: //*[(last() - position() + b - 1) mod a = 0][name() = "E"]
Элемент E, который является a*n+b ребенком с конца своего родителя
E:first-child //*/*[1][name() = "E"] или
//*/*[1]/self::E
Элемент E, который является первым ребенком своего родителя
E:last-child //*/*[last()][name() = "E"] или
//*/*[last()]/self::E
Элемент E, который является последним ребенком своего родителя
E:only-child //*[count(*) = 1]/E Элемент E, который является единственным ребенком своего родителя
E:empty //E[count(*) = 0 and (not(text()) or text() = "")] Элемент E, у которого нет детей, включая текстовых нод
E:checked //E[@checked] -
E:enabled //E[@disabled="false"] -
E:disabled //E[@disabled="true"] -
E.warning //E[contains(concat(" ", @class, " "), concat(" ", "warning", " ")] Элемент E, у которого есть класс warning
#myid id(«myid») Элемент c id равным myid
E#myid //E[@id="myid"] Элемент E, у которого id равен myid
E:not(s) //E[not(_selector_)] Отрицательный селектор
E F //E//F Элемент F, который является потомков E
E > F //E/F Элемент F, который является ребенком E
E + F //E/following-sibling::*[1]/self::F Элемент F, который непосредственно следует за E
E ~ F //E/following-sibling::F Элемент F, который следует за E
взято тут

четверг, 3 мая 2012 г.

Учим Properties понимать русский язык

В ходе проекта передо мной возникла необходимость подгружать некоторые значения из файлов. Наиболее простым и быстрым решением показалось считывать эти файлы как объекты Properties.

Но вылезла неприятная особенность, метод Properties.load() по умолчанию загружает все данные в кодировке ISO-8859-1. Это не есть хорошо так как файлы должны содержать кирилицу и после загрузки таким макаром она превращалась в набор кракозябр.

Решение оказалось довольно таки очевидным:

Properties props=new Properties();
props.load(new InputStreamReader(new FileInputStream("system.props"), "UTF-8"));

При загрузке таким образом кириллица остается кириллицей ))