Содержание


Grester облегчает JUnit-тестирование Java-приложений

Grester - это оболочка Apache Maven для Jester, которая проверяет код, написанный без учета требований разработки через тестирование

Comments

Jester, созданный Айвеном Муром (Ivan Moore), представляет собой превосходный инструмент, который тестирует unit-тесты, написанные программистами и разработчиками. Он основан на предположении, что в коде может существовать множество мест, содержащих операторы условных переходов, циклов и выбора, а также мест, в которых цикломатическая сложность классов в целом может резко возрастать или увеличиваться из-за множества возможных путей исполнения. Jester сосредоточен именно на таких участках кода. Но для его работы требуется хорошо отформатированный classpath к различным ресурсам.

Grester, который представляет собой оболочку Apache Maven вокруг Jester, упрощает рутинную работу по созданию classpath Java™ с учетом зависимостей проекта, облегчая тестирование с применением Jester. Кроме этого, Grester пытается реализовать некоторые преимущества Maven, который лежит в основе его инфраструктуры. Jester чрезвычайно полезен в качестве дополнительного средства проверки кода, написанного без учета требований разработки через тестирование. Это могут быть старые программы или код, недавно написанный программистами, которые считают методы разработки через тестирование, входящие в концепцию гибкой разработки ПО (Agile software development), слишком грубыми в качестве исходного принципа написания качественного кода.

На самом деле Grester можно использовать для демонстрации ограничений, с которыми сопряжено написание кода без применения метода разработки через тестирование. По моему опыту, постепенное изменение требований и код, написанный без учета того, что должно служить его истинной бизнес-функцией, увеличивают число ошибок и объем pack bay (что быстро приближает нас к антимодели blob, даже в небольших фрагментах кода, не обязательно выполненных в виде одного трудноуправляемого модуля или набора модулей).

В этой статье мы не будем вдаваться в технические детали интерпретации выходных данных Jester и приводить подробное описание его работы. За этой информацией обращайтесь к разделу Ресурсы, где приведена ссылка на превосходную статью Эллиотта Расти Гарольда (Elliott Rusty Harold), или посетите сайт сайт Айвена Мура. Здесь же приводятся рекомендации по приобретению и использованию Maven-модуля, выступающего оболочкой для Jester.

Где взять Grester

Grester можно взять из двух источников — оба они приведены в разделе Ресурсы. Необходимая для его работы инфраструктура минимальна: для его создания и применения нужен лишь Maven. Grester написан на Groovy, динамическом языке с Java-подобным синтаксисом, обладающем преимуществами таких языков, как Python и Ruby. В своей основе Grester представляет собой просто модуль Maven, предназначенный для быстрого исполнения инструмента Jester, так что всю свою силу Grester черпает из Jester. В примерах для этой статьи использовался Jester V1.37 с альфа-версией Grester V0.3.

Jester без рутины: почему бы и нет?

Если все зависимости проекта Java Archive (JAR) находятся в одном и том же месте, для прямого исполнения Jester достаточно указать единственный каталог в записи Java classpath. Когда же зависимости разбросаны по файловой системе, конфигурирование Jester для каждого прогона может стать чрезвычайно сложным и неприятным делом, особенно если места расположения отдельных зависимостей меняются. Maven существенно облегчает этот процесс.

Jester работает в каждом экземпляре снаружи от конфигурации сборки проекта Maven. Так в чем же особенность Grester? Дело в том, как Maven организует свои зависимости. Он не только старается сделать так, чтобы эта «компоновка» не оказалась неэффективной, но и предпринимает попытки стандартизовать как состояние, в котором находятся JAR (и, следовательно, Groovy) и Web-архивы (WAR) Java, так и место их нахождения.

Для тех, кто не знаком с Maven, в нем используется идея репозитария. Существует локальный репозитарий по умолчанию, расположенный по адресу $USER_HOME\.m2\repository, и удаленный репозитарий, сконфигурированный в файле pom.xml или settings.xml, расположенный по адресу $MAVEN_HOME/conf.

Установка Grester

Найдите по ссылке сжатый TAR-файл (файлы .tar и tar.gz предназначены для UNIX® и Linux®) или файл ZIP для Microsoft® Windows® и распакуйте его. Это можно сделать несколькими способами: здесь я использую пакет Cygwin под Windows.

Рисунок 1. Извлечение Grester при помощи Cygwin под Windows
Извлечение Grester при помощи утилиты Cygwin под Windows
Извлечение Grester при помощи утилиты Cygwin под Windows

Можно также использовать утилиту TAR с опциями xzvf для tar.gz и опциями xvf для неформатированного файла .tar. Пример приведен на рис. 2.

Рисунок 2. Извлечение файла Grester tar.gz при помощи утилиты TAR
Извлечение файла Grester tar.gz при помощи утилиты TAR
Извлечение файла Grester tar.gz при помощи утилиты TAR

Окончательная структура каталога должна выглядеть, как на рис. 3.

Рисунок 3. Извлеченный Grester в Windows
Извлеченный Grester в Windows
Извлеченный Grester в Windows

Конфигурирование, компоновка и установка Grester

Теперь пора сообщить Maven о внешних репозитариях, из которых можно извлечь соответствующие зависимости Grester Groovy, чтобы скомпилировать и установить Grester как локальный модуль Maven. Для этого добавьте в файл $MAVEN_HOME/conf/settings.xml два удаленных репозитария, как показано ниже.

Листинг 1. Указание Maven на два удаленных репозитария с зависимостями Groovy
<settings>
 <profiles>
  <profile>
   <id>repositoryDefinitions</id>
   <repositories>
   .....
   .....
   <!—У вас могут быть другие зависимости -->
   ....
   ....
   <repository>
    <id>apache-snapshotsv/id>
    <name>Apache Snapshots Repository</name>
    <url>http://people.apache.org/repo/m2-snapshot-repository</url>
    <layout>default</layout>
    <snapshots>
     <enabled>true</enabled>
      <updatePolicy>daily</updatePolicy>
      <checksumPolicy>ignore</checksumPolicy>
    </snapshots>
    <releases>
     <enabled>false</enabled>
    </releases>
   </repository>
   .....
   .....
   </repositories>
   ...
  </profile>
 </profiles>
</settings>

Затем следует конфигурирование модуля для Maven с указанием на репозитарий для зависимостей модуля Grester Groovy. Эта конфигурация модуля-репозитария размещается в том же профиле, который был определен для репозитариев (например, здесь в качестве имени профиля используется repositoryDefinitions), как показано ниже.

Листинг 2. Указание Maven на удаленные репозитарии, содержащие зависимости модуля Groovy
<settings>
 <profiles>
  <profile>
   <id>repositoryDefinitions</id>
   ....
   ....
   </repositories>
   <pluginRepositories>
   <!-- У вас могут быть другие зависимости модуля -->
   ....
   ....
    <pluginRepository>
     <id>apache-snapshots</id>
     <name>Apache Snapshots Repository</name>
     <url>http://people.apache.org/repo/m2-snapshot-repository</url>
     <layout>default</layout>
     <snapshots>
      <enabled>true</enabled>
      <updatePolicy>daily</updatePolicy>
      <checksumPolicy>ignore</checksumPolicy>
     </snapshots>
     <releases>
      <enabled>false</enabled>
     </releases>
    </pluginRepository>
    ...
    ...
   </pluginRepositories>
   ...
  </profile>
 </profiles>
</settings>

Теперь, наконец, можно окончательно скомпоновать модуль. Для Grester требуется Maven V2.0.5 или более поздняя версия. При использовании более ранних версий возникнут проблемы компиляции и использования функциональности в зависимости Groovy-mojo-support. Если каталог $MAVEN_HOME/bin является частью системного пути исполняемых файлов, выполните команду mvn clean install из каталога maven-grester-plugin (каталог, содержащий файл Grester pom.xml), как показано ниже.

Рисунок 4. Компоновка Grester из командной строки
Компоновка Grester из командной строки
Компоновка Grester из командной строки

Компоновка обычно выполняется быстро (менее 20 с). На рис.5 показано окно после успешной установки.

Рисунок 5. Установка Grester в локальном депозитарии Maven
Установка Grester в локальном депозитарии Maven
Установка Grester в локальном депозитарии Maven

Важно, где именно устанавливается Grester в локальном репозитарии Maven. Для тех, кто еще не знаком с Maven, его локальным депозитарием по умолчанию является $USER_HOME/.m2/repository/. В системе под Windows по умолчанию $USER_HOME, вероятнее всего, будет означать Documents and Settings/$USERNAME/ (где $USERNAME - имя пользователя, указываемое при регистрации в системе). На машине Linux/UNIX $USER_HOME означает /home/$USERNAME/. Беглый взгляд на локальный репозитарий Windows показывает, что Grester установлен в каталоге C:/Documents and Settings/$USERNAME/.m2/repository/org/apache/maven/plugins, и создан каталог с именем maven-grester-plugin. Он содержит каталог с номером версии (последней была версия V0.3); а внутри этого каталога находится собственно файл maven-grester-plugin-x.x.jar.

Причина такой структуры заключается в файле Grester pom.xml. Как видно из рис.6, идентификатором groupId проекта Grester служит org.apache.maven.plugins. Любые модули Maven, написанные на языке Java или Groovy и содержащие эту строку в качестве значения groupId, содержат формулы, которые легче исполнить из командной строки, чем модули Maven с каким-то другим произвольным значением groupId. Так как Grester использует именно эту строку, при исполнении отдельных целей mojo из командной строки не надо приставлять groupId и artifactId в начале.

Рисунок 6. groupId Grester в файле конфигурации pom.xml
Grester pom.xml groupId
Grester pom.xml groupId

После инсталляции создается каталог maven-grester-plugin (его создает цель install), как показано ниже. Другие стандартные модули Maven устанавливаются в тот же родительский каталог, например, maven-surefire-plugin или maven-install-plugin.

Рисунок 7. Grester в локальном репозитарии Maven
Grester в локальном репозитарии Maven
Grester в локальном репозитарии Maven

Использование специальной строки groupId полезно, когда особые идентификаторы артефакта и группы проекта либо слишком длинны и трудны для запоминания, либо просто неудобны для многократного набора на клавиатуре. Вот почему для исполнения основных целей Maven из его модулей по умолчанию (например, maven-compiler-plugin или maven-surefire-plugin), таких как compile, test или даже test-compile, не требуется команд типа: mvn org.apache.maven.plugin:maven-compiler-plugin:2.0.2:compile или mvn org.apache.maven.plugin:maven-surefire-plugin:2.3:test (достаточно mvn compile или mvn test).

Установка Jester в качестве главной зависимости Grester

Теперь у нас есть все, кроме самого Jester, который составляет основу Grester. На платформах Windows и Linux/UNIX есть два удобных сценария, позволяющих установить Jester (то есть, фактически, файл jester-1.37.jar) в локальный репозитарий Maven. Зачем они нужны? Нельзя ли просто загрузить их из тех же внешних источников, что и Maven, когда тот получает свои зависимости для компилятора, инсталлятора и других модулей? Дело в том, что в общедоступных и общеизвестных репозитариях Maven (таких как Ibiblio for Maven) Jester отсутствует, так что нельзя сконфигурировать файл Maven $MAVEN_HOME/conf/settings.xml с удаленным репозитарием, содержащим Jester (независимо от способа, которым он был установлен с комбинацией groupId-artifactId-версия).

Поэтому для Windows и Linux/UNIX соответственно предлагаются исполняемые файлы install-jester.bat и install-jester.sh. При их исполнении на соответствующей платформе можно воспользоваться приведенной ниже командой.

Рисунок 8. Установка зависимости для Jester
Установка зависимости для Jester
Установка зависимости для Jester

Примечание: уже после написания этой статьи в общедоступном репозитарии Maven был опубликован Grester V1.0.1. Это означает, что теперь модуль можно получить прямо из хорошо известного репозитария Apache, но для завершения полной картины все равно требуется ядро Jester JAR и инструкции по совместному использованию.

Использование Grester в примере проекта Maven

Итак, у вас есть готовый проект Maven, и вы хотели бы испытать Jester на своих unit-тестах (или хотя бы на группе тестов). Независимо от того, какие это тесты - unit-тесты или интеграционные (integration tests), лучше скопировать проект где-нибудь в файловой системе и выполнять Jester с этой копией или использовать существующую копию, но быть готовым к отмене любых изменений в файлах исходного кода. Дело в том, что Jester изменяет файл исходного кода, сохраняет измененную версию и перекомпилирует код (оставляя файл классов в том же каталоге, что и исходный файл). Если объем кода проекта относительно невелик или выбрано немного тестов, можно использовать существующую копию.

Установка файлов примера в Eclipse

Для примера воспользуемся простым проектом Maven, подготовленным в интегрированной среде разработки Eclipse. Рассмотрение того, как именно составлялся проект Maven и создавались необходимые файлы в среде разработки, выходит за рамки этой статьи, но в разделе Ресурсы приведены ссылки и информация о том, как это делается. На рис.9 показан проект в интегрированной среде разработки Eclipse.

Рисунок 9. Пример проекта Maven в интегрированной среде разработки Eclipse
Пример проекта Maven в IDE Eclipse
Пример проекта Maven в IDE Eclipse

В качестве примера воспользуемся относительно простым классом и соответствующим тестом этого класса. Данный класс выполняет команду управления внешним процессом с применением языка Java. В листинге 3 показаны основные части тестируемого класса.

Листинг 3. Пример тестируемого класса в проекте Maven
package com.prometheus.run;
import java.io.IOException;
import java.io.InputStream;
public class CommandExecutor extends Executor{
    ...
    public String executeCommand(String command){
		...
        try {
            Process child = performCommandExecution(command);
            stream = child.getInputStream();
            sb = processStream(stream);
            ...
        }
            ...
        return sb.toString();
    }
    protected StringBuffer processStream(InputStream stream) throws IOException {
         ...
         sb = new StringBuffer();
        while ((c = stream.read()) != -1) {sb.append((char)c);}
        return sb;
    }
   ...
}

Метод executeCommand() внутри класса CommandExecutor осуществляет вызов защищенного метода внутри того же класса, processStream(). Внутри метода processStream() создается новый экземпляр StringBuffer и InputStream, которым можно манипулировать внутри цикла while(). В листинге 4 показан класс теста, опять же приведены только основные части теста.

Листинг 4. Пример класса теста в проекте Maven
package com.prometheus.run;
import com.prometheus.run.CommandExecutor;
...
public class CommandExecutorTest extends TestCase {
	...
	public class MockProcess extends Process{
        ...
        public InputStream getInputStream(){
            String source= "This is a mock string";
            return new ByteArrayInputStream(source.getBytes());
          }
        public OutputStream getOutputStream(){
            return null;
        }
        public int waitFor(){
            return 1;
        }
    }
	public void testExecuteCommmand(){
        String expected = "Это имитация строки";
        String actual = commandExecutor.executeCommand("lsmod");
        assertEquals(expected, actual);
        ...
    }
  }

Класс теста CommandExecutorTest относительно прост. Здесь не приводится его детальное описание, но главная цель этого unit-теста — имитация поведения класса Process посредством обращения к методу performCommandExecution() из тестируемого класса.

Важно отметить, что для успешного исполнения Grester проект должен содержать как исходники программы, так и исходники тестов, и все тесты должны выполняться успешно. (Заметим, что по этой причине исполнение Grester допустимо лишь начиная с этапа Maven test-compile). Следующим шагом нужно просто добавить конфигурацию модуля Maven для Grester в файл pom.xml. Эта конфигурация помещается в раздел сборки по умолчанию (default build) файла pom.xml или в любой допустимый профиль Maven.

Введение Grester в проект

Листинг 5 иллюстрирует пример конфигурации модуля Grester, помещенного в файл проекта pom.xml. Обратите внимание на то, что groupId соответствует org.apache.Maven.plugins и что в качестве версии указана последняя версия модуля Grester: V0.3.

Листинг 5. Конфигурирование модуля Grester в примере проекта
<plugins>
...
...
<!-- START MAVEN GRESTER PLUG-IN CONFIGURATION -->
<plugin>
<groupId>org.apache.Maven.plugins</groupId>
<artifactId>Maven-Grester-plugin</artifactId>
<version>0.3</version>
<configuration>
<codeSources>src/main/java/com/prometheus/run</codeSources>
<testSuiteClass>com.prometheus.run.CommandExecutorTest</testSuiteClass>
</configuration>
<executions>
<execution>
<id>inspectSourcesCodeWithGrester</id>
<phase>test</phase>
<goals>
<goal>inspect</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- END MAVEN GRESTER PLUG-IN CONFIGURATION -->
...
</plugins>

Заметьте, что проект ориентирован на выполнение цели Grester inspect на этапе тестирования Maven. Путь codeSources указывает на каталог, содержащий исходники, для которых существует класс тестирования CommandExecutorTest. Точно так же он мог бы указывать на реальный класс CommandExecutor с поправкой на расширение имени файла. В файле README.txt, прилагаемом к Grester, упоминается расширение .Groovy, но надо отметить, что на данный момент поддержки использования Grester с исходниками Groovy не существует.

Альфа-версия Grester V0.3 в виде модуля имеет две главные цели (при использовании все буквы должны быть строчными), исполняемые на любом допустимом этапе жизненного цикла Maven:

  • inspect— Это главная цель Grester, обычно исполняемая на этапе тестирования (хотя строго говоря, это может быть любой этап, следующий за этапом test-compile). Grester создает действительный путь Java из отношений, перечисленных в файле pom.xml, а затем передает новый путь Jester.
  • help— Эта цель используется главным образом для справок по поводу правильного синтаксиса и структуры модулей и может исполняться отдельно из командной строки командой mvn grester:help.

Прогон Grester на примере проекта

Выполнение простой команды mvn clean install (или любой команды жизненного цикла, содержащей определенный этап, который использует цель inspect) приводит к показанному ниже результату.

Рисунок 10. Jester за обработкой примера кода
Jester за обработкой примера кода
Jester за обработкой примера кода

При детальном рассмотрении видно, что строка 27 оригинального файла класса CommandExecutor изменена с -1 на 1. Для полного выполнения операции над классом может потребоваться некоторое время. В конце операции создается файл jesterReport.xml с описанием хода процесса в окне Java Swing.

Вызов справки Grester

Выполнение команды mvn grester:help из командной строки приводит к листингу, аналогичному показанному на рис. 11. Это краткая справка по конфигурированию Grester без обращения к оригинальному файлу README.txt.

Рисунок 11. Цель справки Grester
Цель справки Grester
Цель справки Grester

Заключение

Grester не является идеальным модулем и продолжает совершенствоваться. Особенно полезной была бы прямая поддержка исходников Groovy. Ту же идею можно было бы применить к проектам, в которых не используется Maven, но все же требуется создание строки пути Java, например, для файлов сборки Apache Ant, в которых указываются зависимости с использованием разных каталогов в одной и той же файловой системе. Если же сами файлы Ant разбиты на множество отдельных файлов, процесс может усложниться.

Трудно сказать, стоит ли на самом деле возиться с применением одного инструмента (Jester) к процессу, зависимости которого невозможно легко идентифицировать в одном месте, но мне кажется, что Jester — хороший инструмент для проверки качества написания тестов программистами. Ведь когда отчет Jester показывает низкое качество unit- или интеграционного теста из-за внесения существенных изменений в код с использованием статического набора тестов, это ставит под сомнение квалификацию программиста в области разработки через тестирование (Test-Driven Development - TDD) и даже разработки на основе поведения (Behaviour-Driven Development - BDD).


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=416839
ArticleTitle=Grester облегчает JUnit-тестирование Java-приложений
publish-date=07282009