logo

Thursday 23rd of February 2012

Статистика сайта


Сегодня сайт посетило:7
Вчера:110
За этот месяц:2283
За этот год:5653
Всего:50656

Apache Ant Framework - автоматизация сборок PDF Печать E-mail
Автор: Mariya Sokunova   
20.02.2011 10:02

Автоматизация процесса сборки java-проектов с помощью Ant.

Из данной статьи Вы узнаете, как с помощью Apache Ant framework автоматически собирать Java проект, запускать тесты и многое другое. Ant - это framework, с помощью которого можно избавиться от стандартного набора команд:
# javac ...
# java ...

которые приходится выполнять после изменений в коде. Для этого Вам нужно будет 1 раз настроить xml файл для проекта с соответствующими заданиями. А далее запускать его либо вручную, либо через crontab либо через CI(Continuous Integretion) систему.

Установка и настройка Ant

  1. Скачиваем последнюю версию Ant.
  2. Распаковываем архив в какую-нибудь папку, к примеру, /home/ant/apache-ant-1.8.2
  3. Прописываем переменные окружения:
    export ANT_HOME=/home/ant/apache-ant-1.8.2
    export PATH=$PATH:$ANT_HOME/bin
    export JAVA_HOME=/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0
  4. Перечитываем настройки окружения:
    # source /root/.bash_profile

Основы Ant

Смысл использования Ant в том, что в зависимости от этапа разработки(цикла) Вам необходимо выполнять различные виды сборок. К примеру, просто откомпилировать классы, сделать проверку с помощью unit-тестов, сгенерировать javadoc. Или выполнить все одновременно. Этим и занимается Ant - он позволяет настроить файл с заданиями и их комбинациями, которые можно вызывать последовательно.
Концепции Ant :

  • build.xml - Файл сборки ассоциируется с проектом.На 1 проект создается 1 файл xml - файл сборки. В принципе его можно называть, как будет угодно, но по-умолчанию это build.xml. Если назовете свой файл по-другому, к примеру, mybuild.xml, то для запуска задач из этого файла нужно будет всегда указывать его название:
    # ant -f mybuild.xml
    Если же Вы назовете свой файл build.xml, то параметр -f имя_файла можно опускать, и соответственно вызов файла будет следующим:
    # ant
  • target - задание (цель). В файле build.xml должно быть хотя бы одно задание. Задания могут зависеть от других заданий. К примеру, собрать jar файл можно только после компиляции. Задания можно запускать по-отдельности или сразу несколько.
  • property - позволяют задать переменные сборки. Такие как папки с исходными кодами, /tmp и т.д. К свойствам можно обращаться при помощи ${property_name}.

Для запуска самого простого задания с помощью ant, необходимо создать файл build.xml в папке с проектом. И заполнить его:

<project name="EnglishTesting" default="test">
<target name="test" description="Start test" >
<echo>Performing of the tests....</echo>
</target>
</project>

Таким образом мы создали задание "test" с одной командой "echo".
При запуске Ant созданного Вами задания приложение должно выдать что-то вроде:

# ant -f build.xml test
Buildfile: /home/masha/java/EnglishTesting/build.xml

test:
[echo] Performing of the tests....

BUILD SUCCESSFUL
Total time: 1 second

Называть задания можно как угодно, но лучше использовать следующие рекомендуемые наименования, которые для всех других разработчиков ясны и понятны:

  • test - Запуск junit тестов.
  • clean - Очистка (удаление) всех временных папок и файлов (к примеру, папка сборки).
  • deploy - Установка(копирование) jar файлов в нужную директорию, где выполняемые файлы, можно например запускать и использовать для интеграционного тестирования.
  • compile - Компиляция проекта.
  • javadoc - Генерация документации к разработанным классам.
  • all - выполнение всех действий последовательно, например, clean затем compile затем javadoc затем jar и deploy.
  • jar - генерация java архива всего проекта.

И некоторые другие. Подробнее можете прочитать, к примеру, в книге "Professional Java Tools for Extreme Programming".

 

Реализуем компиляцию проекта

Рассмотрим простое задание - скомпилируем наш проект в нужной директории. Для этого создадим следующий build.xml файл:

<project name="EnglishTesting" default="compile">
<property name="src.java" location ="src"/>
<property name="build.dir" location="build"/>
<property name="build.classes.java" location="${build.dir}/class/java"/>
<target name="make.dirs" description="Making of the auxillary dirs" >
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.classes.java}"/>
</target>
<target name="compile" description="Compilation of the classes" depends="make.dirs">
<echo>Compilation ....</echo>
<javac includeantruntime="false" destdir="${build.classes.java}">
<src path="${src.java}"/>
</javac>
</target>
</project>

Как Вы заметили мы задали набор property , где указали директории исходников, релизов. Атрибут location (value) - хранит значение переменной. Если Вы создаете переменную для хранения имени директории, то нужно указывать location, если нужно использовать переменную в качестве имени проекта, то используйте value.
Далее мы выполняем задание make.dirs. В нем мы вызываем комады для создания нужных нам директорий. И только после этого мы выполняем задание compile, так как в нем есть атрибут depends. Атрибут depends позволяет нам управлять заданиями.
Команда javac позволяет собрать проект. Атрибут destdir указывает, где нужно расположить откомпилированные классы. Атрибут src path="${src.java}" указывает где взять исходники. includeantruntime - атрибут отвечающий за подключение библиотек лежащих в lib самого Ant. Лучше всегда указывать false, чтобы выполнение заданий не зависило от окружения - от машины на которой запускается build.xml.
Запустим build.xml файл:

 

# ant compile.java
Buildfile: /home/masha/java/EnglishTesting/build.xml

make.dirs:
[mkdir] Created dir: /home/masha/java/EnglishTesting/build
[mkdir] Created dir: /home/masha/java/EnglishTesting/build/class/java

compile.java:
[echo] Compiling classes...
[javac] Compiling 4 source files to /home/masha/java/EnglishTesting/build/class/java

BUILD SUCCESSFUL
Total time: 1 second

Заметьте, если повторно запустить на выполнение эту же команду, то будет выдано следующее:

# ant compile.java
Buildfile: /home/masha/java/EnglishTesting/build.xml

make.dirs:

compile.java:
[echo] Compiling classes...

BUILD SUCCESSFUL
Total time: 0 seconds

Это значит, что если папки уже существуют, то они повторно не пересоздаются. А также если в коде не было изменений, то он также повторно не компилируется.

 

Тестирование проекта

Для запуска тестов используется тег junit. Он имеет множество удобных настроек. Рассмотрим запуск простого теста на следующем примере. Для этого допишем в наш предыдущий build.xml файл новую задачу test:

<target name="compile.test" description="Compilation of the tests" depends="make.dirs, compile">
<echo>Compiling tests....</echo>
<javac includeantruntime="false" destdir="${build.classes.test}">
<src path="${src.test}"/>
<classpath>
<pathelement location="${classpath.dir}/junit-4.5.jar"/>
<pathelement location="${build.classes.java}"/>
</classpath>
</javac>
</target>
<target name="test" Start test EnglishTesting class" depends="compile.test">
<junit printsummary="yes" haltonerror="yes" haltonfailure="yes" fork="yes">

<formatter type="plain" usefile="false"/>
<test name="testing.EnglishTestingTest"/>
<classpath>
<pathelement location="${classpath.dir}/junit-4.5.jar"/>
<pathelement location="${build.classes.java}"/>
<pathelement location="${build.classes.test}"/>
</classpath>
</junit>
</target>

Свойства ${build.classes.test}, ${classpath.dir} зададим заранее. Теперь запустим наш файл сборки:

# ant test
Buildfile: /home/masha/java/EnglishTesting/build.xml

make.dirs:
[mkdir] Created dir: /home/masha/java/EnglishTesting/build
[mkdir] Created dir: /home/masha/java/EnglishTesting/build/class/java
[mkdir] Created dir: /home/masha/java/EnglishTesting/build/class/test
[mkdir] Created dir: /home/masha/java/EnglishTesting/build/jar

compile:
[echo] Compiling classes...
[javac] Compiling 4 source files to /home/masha/java/EnglishTesting/build/class/java

compile.test:
[echo] Compiling tests....
[javac] Compiling 2 source files to /home/masha/java/EnglishTesting/build/class/test


test:
[junit] Running testing.EnglishTestingTest
[junit] Testsuite: testing.EnglishTestingTest
[junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.061 sec
[junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.061 sec
[junit]
[junit] Testcase: testCalcPercentNullQuestions took 0.007 sec
[junit] Testcase: testCalcPercent took 0.001 sec
[junit] Testcase: testCalcSkillIncrement took 0.001 sec
[junit] Testcase: testCalcSkillIncrementErrors took 0 sec
[junit] Testcase: testGetResultsExcelent took 0.001 sec
[junit] Testcase: testGetResultsGood took 0 sec
[junit] Testcase: testGetEnglishWord took 0 sec
[junit] Testcase: testCheckAnswer took 0.001 sec
[junit] Testcase: testCheckIncorrectAnswer took 0 sec
[junit] Testcase: testGenerateAnswerForImproperWord took 0 sec

BUILD SUCCESSFUL
Total time: 3 seconds

Рассмотрим атрибуты junit более подробно.

  • printsummary="yes" - в конце прогона будет выдана сумма пройденных и не пройденных тестов
  • haltonerror="yes" - остановить процесс сборки при возникновении ошибки ( unexpected exception)
  • haltonfailure="yes" - остановить процесс сборки, если условие assert вернуло false
  • fork="yes" - запуск на отдельной JVM

Также можно задать и дополнительные параметры JVM, такие как maxmemory размер памяти и timeout время на выполнение тестов. Элемент test name позволяет указать нам имя класса с тестами для запуска. Так как unit-тесты зависят от внешних ресурсов, таких как сама библиотека junit.jar и исходников нашего проекта, то при их компиляции мы обязательно должны указывать элемент classpath. Т.е. указать junit, где искать необходимые библиотеки. Подключить classpath можно двумя способами:
1) как указано у нас

<javac ...>
<classpath>
<pathelement location="${classpath.dir}/junit-4.5.jar"/>
<pathelement location="${build.classes.java}"/>
</classpath>
</javac>

2) либо

<project ... >
<path id="project.class.path">
<pathelement location="${classpath.dir}/junit-4.5.jar" />
<pathelement location="${build.classes.java}"/>
</path>
<target ... >
<javac ...>
<classpath refid="project.class.path"/>
</javac>
</target>
</project>

Первый способ удобен, когда у Вас маленький проект и Вам нужно быстро набросать сборочный файл. Но лучше пользоваться вторым способом. Второй способ позволяет сэкономить, если придется указывать classpath и в других задачах java или javac.
Элемент formatter type отвечает за формат результатов прогона тестов. Значение type может быть brief - краткий текстовый, plain - подробный текстовый и xml. Атрибут usefile позволяет выводить результаты прогона тестов на консоль или в файл.

 

Генерация отчета html о прохождении тестов

Как говорилось выше результаты задания junit могут выводиться либо на консоль либо в файл. Для генерации html отчета необходимо создать задания следующего вида:

<target name="testsuite" description="Start TestSuite class" depends="compile.test">
<junit printsummary="yes" haltonerror="yes" haltonfailure="yes" fork="yes">
<formatter type="xml"/>
<test name="suite.SuiteTests" todir="${html.report.dir}"/>
<test name="testing.EnglishTestingTest" todir="${html.report.dir}"/>
<classpath>
<pathelement location="${build.classes.java}"/>
<pathelement location="${build.classes.test}"/>
</classpath>
</junit>
</target>
<target name="report" description="" depends="testsuite">
<mkdir dir="${html.report.dir}/html"/>
<junitreport todir="${html.report.dir}" >
<fileset dir="${html.report.dir}">
<include name="TEST-*.xml"/>
</fileset>
<report todir="${html.report.dir}/html"/>
</junitreport>
</target>

Первое, что мы делаем это сохраняем результаты проведенных тестов в XML. Далее мы используем элемент junitreport, он преобразует полученный xml в html в указанную в атрибуте todir директорию. Элемент fileset позволяет junitreport указать, какие именно xml файлы использовать для генерации html. Результаты в броузере можно посмотреть открыв index.html:

 

Сборка jar архива

Каждый JAR файл содержит в себе файл MANIFEST.MF, который лежит в директории META-INF. MANIFEST.MF это текстовый файл, который содержит информацию о JAR архиве, например версию, конфигурацию, запускаемый класс. Сгенерируем JAR файл для нашего приложения с помощью следующего задания:

<target name="jar" description="Creating jar after tests" depends="compile, test">
<jar destfile="${build.jar}/EnglishTesting.jar" basedir="${build.classes.java}">
<manifest>
<attribute name="Main-Class" value="userconsole.ViewTesting"/>
<attribute name="Class-Path" value="${classpath.dir}/mysql-connector-java-5.1.14-bin.jar"/>
<attribute name="Created-By" value="(с) Mariya Sokunova"/>
</manifest>
</jar>
<target/>

В нашем случае будем генерировать JAR файл только после корректного прохождения unit-тестирования. В MANIFEST.MF зададим атрибуты:
"Main-Class" - указываем класс, который является входной точкой вашего приложения. Класс должен содержать метод main().
"Class-Path" - перечисляем все библиотеки, от которых зависит наше приложение.
"Created-By" - указываем, кем было создано приложение.
Теперь можно запустить наше приложение так:
java -jar EnglishTesting.jar

 

Запуск проекта

Если для проверки работоспособности Вашего приложения необходимо осуществляеть его запуск, то можно воспользоваться следующим заданием:

<target "run" description="Run application" depends="jar">
<echo>Running application . . . </echo>
<java jar="${build.jar}/EnglishTesting.jar" fork="true"/>
<target/>

Заметьте, если Вы используете консольное приложение, то оно запускается в отдельном процессе, а не в том в котором запущен Ant. Поэтому вводить данные через консоль при этом не удастся.

 

Генерация документации

Когда проект готов Вы возможно захотите сформировать для него документацию - javadoc. Если Вы используете соответствующие комментарии к классам, то сформировать документацию не сложно. Вам необходимо создать задание :

<target name="javadoc" description="Creating javadoc" depends="make.dirs">
<echo>Generation javadoc . . . </echo>
<javadoc destdir="${javadoc.dir}" sourcepath="${src.java}" author="true" version="true"
packagenames="*" windowtitle="EnglishTesting" use="true" >
<bottom>
<![CDATA[<b>Sokunova Mariya, 2011<b>]]>
</bottom>
</javadoc>
</target>

После запуска задания Вы получите готовую javadoc документацию :

 

Полный пример build.xml можно взять здесь. Более подробно об инструменте Apache Ant можно почитать здесь.

Обновлено 20.02.2011 10:13
 
Unit и Coverage тестирование Java проектов PDF Печать E-mail
Автор: Mariya Sokunova   
07.02.2011 17:43

Unit и Coverage тестирование Java проектов

Описание Java проекта

Приложение EnglishTesting создано для тестирования английского языка. Оно выводит вопрос - Английское слово и запрашивает от пользователя перевод. Слова хранятся в БД(mysql). Для того чтобы приложение заработало Вам необходимо :

  1. Создать БД : CREATE DATABASE englishwords CHARACTER SET utf8;
  2. Создать в БД нужную структуру(файл есть в архиве): # cat englishwords.sql | mysql -uuser -ppasswd englishwords
  3. Заполнить БД тестовыми данными: # cat dump_words.sql|mysql -uuser -ppasswd englishwords

Схема приложения показана ниже:

 

Также Вам необходимо скачать JDBC driver для MYSQL и прописать его в конфигурационном файле:
CLASSPATH=./:/home/JavaClasses:/home/JavaClasses/EnglishTesting.jar:/home/JavaClasses/mysql-connector-java-5.1.14-bin.jar
После всех корректных настроек(также проверьте правильность подключения драйвера в проекте) использовать приложение можно следующим образом:
# java userconsole.ViewTesting
Вопрос № 1
howling
Перевод :
....
Дата: Sat Feb 05 19:19:08 MSK 2011
Результат 50%
Правильных ответов 2 из 4
Ошибок 2
Неправильный ответ на вопрос № 2
Слово perform
Ваш ответ .
Правильный ответ [выполнять, исполнять, делать]
...
Исходный код проекта EnglishTesting.tar.gz.

Unit-тесты с использованием заглушек

Теперь покроем юнит-тестами созданный класс EnglishTesting.java. Для того, чтобы протестировать методы( классы), которые в свою очередь зависят от других классов необходимо использовать заглушки (stub).
Так как класс EnglishTesting зависит от класса DataConnection, а нам нужно протестировать именно работоспособность EnglishTesting, то мы с помощью интерфейса DataConnection в тестовом классе EnglishTestingTest создадим заглушку на DataConnection. Таким образом, всегда можно протестировать классы независимо друг от друга - в этом и есть суть юнит-тестирования.
Начнем с метода, который использует класс DataConnection в качестве переменной. Это метод getEngWord():

public class EnglishTesting {
...
  public String getEngWord(DataConnection data){
    String englishWord;
    englishWord = data.returnRandomWord();
    return englishWord;
  }
...
}

Этот метод просто возвращает слово, полученное от объекта data методом returnRandomWord().
Теперь создадим заглушку StubDBconnect :

public class StubDBconnect implements DataConnection{
  public String returnRandomWord() {
    return "Hello";
  }
  public ArrayList returnAnswersByWord(String englishWord) {
    ArrayList answersList = new ArrayList();
    answersList.add("привет");
    return answersList;
  }
} 

Теперь наш Test Case c одним тестовым методом будет выглядеть следующим образом:

package testing;
import connection.DataConnection;
import java.util.ArrayList;
import org.junit.Test;
import static org.junit.Assert.*;
public class EnglishTestingTest {
  /**
   * Stub class - Data Connection
   */
  public class StubDBconnect implements DataConnection{
    public String returnRandomWord() {
      return "Hello";
    }
    public ArrayList returnAnswersByWord(String englishWord) {
      ArrayList answersList = new ArrayList();
      answersList.add("привет");
      return answersList;
    }
  }
  @Test
  public void testGetEnglishWord(){       
    StubDBconnect data = new StubDBconnect();
    EnglishTesting test = new EnglishTesting();
    String word = test.getEngWord(data);
    assertEquals("Hello",word);        
  } 
}

Таким образом в assertEquals() мы сравниваем эталон - "Hello" - то тестовое слово , которое всегда возвращает заглушка с тем, что получили методом getEngWord(DataConnection data).
В предыдущей статье уже говорилось о возможных вариантах проверок.
Аналогично рассмотрим следующий метод checkAnswer() класса EnglishTesting, который необходимо проотестировать:

public class EnglishTesting {
...
  boolean checkAnswer(String englishWord, String russianWord,DataConnection data){       
    ArrayList answersList = data.returnAnswersByWord(englishWord);
    return answersList.contains(russianWord);
  }
...
}

Данный метод также использует public метод returnAnswersByWord() объекта класса DataConnection. Поэтому для создания unit-теста мы опять будем использовать заглушку:

public class EnglishTestingTest {
  @Test
  public void testCheckAnswer(){        
    StubDBconnect data = new StubDBconnect();
    EnglishTesting test = new EnglishTesting();
    assertTrue(test.checkAnswer("Hello", "привет", data));
  }
...
}

Дадим всем методам класса EnglishTesting пакетный доступ для того, чтобы их можно было покрыть unit-тестами. Для тех, которые используются в Main классе дадим доступ public.
Теперь рассмотрим внутренний метод calcPercent() он использует 2 переменные объекта countQuests и skill и возвращает процент правильных ответов пользователя в качестве строки, например, "50%"

public class EnglishTesting {
...
  String calcPercent(){
    if (countQuests>0){
      double percent = skill.doubleValue()/countQuests.doubleValue();           
      NumberFormat percentFormat = NumberFormat.getPercentInstance();
      percentFormat.setMaximumFractionDigits(1);
      String result = percentFormat.format(percent);
      return result;
    }
    else
    {
      return "100%";
    }        
  }
...
}

Для создания теста этого метода заглушка, нам уже не нужна , но необходимо задать тестовые значения переменных объекта. А именно:

public class EnglishTestingTest {
  ...
  @Test
  public void testCalcPercent(){
    EnglishTesting test = new EnglishTesting();
    //Test data
    test.countQuests=4;
    test.skill = 2;
    assertEquals("50%", test.calcPercent());
  }
...
}

После вынесения объектов data и test в метод @Before наш Test Case примет следующий вид :

package testing;
import org.junit.Before;
import connection.DataConnection;
import java.util.ArrayList;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * JUnit tests for EnglishTesting.java
 * Testing logic which works with DB
 * Testing logic methods getResults(), calcPercent(),calcSkill()
 * @author Sokunova Mariya
 */
public class EnglishTestingTest {
  /**
   * Stub class - Data Connection
   */
  public class StubDBconnect implements DataConnection{
    public String returnRandomWord() {
      return "Hello";
    }
    public ArrayList returnAnswersByWord(String englishWord) {
      ArrayList answersList = new ArrayList();
      answersList.add("привет");
      return answersList;
    }
  }
  private StubDBconnect data;
  private EnglishTesting test;
  @Before
  public void instantiate() {
    test = new EnglishTesting();
    data = new StubDBconnect();       
  }
  //Logic tests
  @Test
  public void testCalcPercentNullQuestions(){
    test.countQuests=0;
    assertEquals("100%",test.calcPercent());
  }
  @Test
  public void testCalcPercent(){
    //Test data
    test.countQuests=4;
    test.skill = 2;
    assertEquals("50%", test.calcPercent());
  }
  @Test
  public void testCalcSkillIncrement(){
    //Test data
    test.countQuests=0;
    test.skill = 0;
    //CorrectAnswer
    test.calcSkill("Hello", "привет", 1, data);
    //Check skills
    assertTrue(test.skill==1);        
  }
  @Test
  public void testCalcSkillIncrementErrors(){
    //Test data
    test.countQuests=0;
    test.errors = 0;
    //IncorrectAnswer
    test.calcSkill("Hello", "пока", 1, data);
    //Check errors
    assertTrue(test.errors==1);
  }
  @Test
  public void testGetResultsExcelent(){
    //Test data
    test.errors = 0;
    test.countQuests =1;
    test.skill=1;
    String result = test.getResults();
    String expect = "Великолепно! \n"+
                    "Результат 100%\n"+
                    "Правильных ответов 1 из 1";
    assertEquals(expect, result);
  }
  @Test
  public void testGetResultsGood(){
    //Test data
    test.errors = 1;
    test.countQuests =2;
    test.skill=1;
    test.improperAnswers="";        
    String result = test.getResults();
    String expect = "Результат 50%\n"+
                    "Правильных ответов 1 из 2\n"+
                    "Ошибок 1\n";
    assertEquals(expect, result);
  }
  //Work with Data
  @Test
  public void testGetEnglishWord(){        
    //StubDBconnect data = new StubDBconnect();
    String word = test.getEngWord(data);
    assertEquals("Hello",word);        
  }
  @Test
  public void testCheckAnswer(){        
    //StubDBconnect data = new StubDBconnect();
    assertTrue(test.checkAnswer("Hello", "привет", data));
  }
  @Test
  public void testCheckIncorrectAnswer(){        
    //StubDBconnect data = new StubDBconnect();
    assertFalse(test.checkAnswer("Hello", "пока", data));
  }
  @Test
  public void testGenerateAnswerForImproperWord(){        
    //StubDBconnect data = new StubDBconnect();
    String result = test.generateAnswerForImproperWord("Hello", "пока", 1, data);
    String standard = "Неправильный ответ на вопрос № 1\n"+
                      "Слово Hello\n"+
                      "Ваш ответ пока\n"+
                      "Правильный ответ [привет]";
    assertEquals(standard,result);
  }   
}

При проверке кода EnglishTestingTest.txt в IDE Netbeans 6.5 Вы получите следующий результат:

Измерение покрытия кода тестами

Для того, чтобы проверить покрытие Unit-тестами исходного кода необходимо использовать какой-нибудь plugin. К примеру, в Netbeans можно установить plugin EMMA (Code Coverage Plugin). Для этого нужно проделать следующие действия:

  1. Выбрать в меню Tools-Plugins. Далее перейти на вкладку Available Plugins и выбрать Code Coverage Plugin, как на рисунке
  2. После этого при нажатии Right Click по проекту в дереве появится возможность выбрать пункт Coverage -> Activate Coverage Collection.
  3. Теперь при нажатии Right Click выбираем пункт Test. Таким образом все наши тесты проходят.
  4. Далее просматриваем результаты прогона тестов, при этом нужно выбрать Coverage -> Show Project Coverage Statistics

    Здесь можно увидеть сколько строк кода покрыто и сколько еще нужно покрыть.

 

Использование Test Suite

Если есть необходимость запускать сразу несколько файлов с тестами одновременно , то можно воспользоваться Test Suite эту возможность также предоставляет JUnit 4.
Для этого Вам нужно создать новый класс, к примеру, SuiteTests, как показано ниже:

//:suite/SuiteTests.java
package suite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
/**
 * JUnit  - suite class
 * @author Sokunova Mariya
 */
@RunWith(value=Suite.class)
@SuiteClasses(value = {
              testing.EnglishTestingTest.class
              })
public class SuiteTests {
}

Suite - это контейнер из нескольких тестов. Для того, чтобы обозначить класс как Suite нужно использовать обозначение @RunWith(value=Suite.class), а также в блоке @SuiteClasses указать те тестовые классы, которые должны запускаться.

PS

В исходниках я также выложила perl скрипт insert.pl для распарсивания words.csv с добавлением новых слов в БД.

Обновлено 11.02.2011 19:02
 
Тестирование GUI с помощью JUnit и Jemmy PDF Печать E-mail
Автор: Mariya Sokunova   
02.01.2011 13:55

Тестирование Swing приложений с помощью Jemmy Toolkit v.2

Swing приложения

Создадим простое Swing приложение, как показано на рисунке:

Для создания Swing приложений нужно подключить соответствующие библиотеки:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Далее в конструкторе SwingApplication() :
1. Создадим объект JFrame:

JFrame myFrame = new JFrame("Button Label Text");

2. Установим для него разметку в виде таблицы GridLayout:

myFrame.setLayout(new GridLayout(0,2,5,5));

где 0 - любое число строк, 2 - число столбцов , 5 зазоры по вертикали и по горизонтали.

3. Расположим компоненты на JFrame:

JLabel myLabel = new JLabel("");
myFrame.add(myLabel);
JTextField myTextField = new JTextField(20);
myFrame.add(myTextField);
JButton myBut = new JButton("First");
myFrame.add(myBut);

4. Добавим обработку действий (событий) таким компонентам, как JTextField и JButton:

SwingApplication(){
...
myTextField.setActionCommand("field1");
myTextField.addActionListener(this);
myBut.addActionListener(this);
...
}//Окончание конструктора
/**Добавляем метод обрабатывающий события кликов, ввода текста и т.д.
*/

public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("First")){
myTextField.setText("First pressed");
}
else if(e.getActionCommand().equals("field1")){
myLabel.setText(myTextField.getText());
}
}

В класе SwingApplication создадим публичный метод, который стартует данное приложение startApp() :

/**
* Method for starting SwingApplication
*/

public static void startApp(){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
new SwingApplication();
}
});
}

Полный код приложения SwingApplication.java.

Для запуска приложения из командной строки, проверяем переменную CLASSPATH:

# env|grep CLASSPATH
CLASSPATH=./:/home/JavaClasses:/home/JavaClasses/jemmy-2.2.7.5.jar:/home/JavaClasses/junit4-4.5.jar

Заметьте jemmy-2.2.7.5.jar и junit4-4.5.jar нам будут нужны для запуска тестов, не забудьте их тоже прописать.
Теперь копируем класс ( с пакетом) в эту папку и компилируем:

# javac /home/JavaClasses/swing/SwingApplication.java

Запускаем откомпилированный java-code Swing приложения на JVM:

# java swing.SwingApplication

Тестирование с помощью сценариев Jemmy v2

Jemmy позволяет искать компоненты на фрейме ( диалоге), создавать события, такие как нажатие кнопок, ввод текста, а также считывать значения из компонентов, таких как Text, Title и т.д.
Компоненты можно искать с помощью методов:
JButtonOperator()
JLabelOperator()
JTextFieldOperator()
...
Т.е. просто прибавляя слово Operator к имени компонента, который необходимо отыскать.
Чем больше параметров Вы знаете о компоненте, тем проще Вам его будет потом найти. А именно, можно найти JButton следующими способами:

  • JButtonOperator(myJFrameOperator) - первый попавшийся на фрейме
  • JButtonOperator(myJFrameOperator,0) - по фрейму и индексу
  • JButtonOperator(myJFrameOperator,"First") - по имени кнопки
  • JButtonOperator(myJFrameOperator,"First",0) - по имени кнопки и индексу ( используется, если к примеру у Вас 2 кнопки с одинаковым именем)

Для использования возможностей Jemmy, необходимо сначала подключить все необходимые библиотеки:

import org.netbeans.jemmy.*;
import org.netbeans.jemmy.operators.*;

Как следует из названия, каждый тест должен иметь интерфейс класса Scenario. Удобнее всего создавать тесты в каком-нибудь IDE.
Создадим пакет test_scenario и создадим в нем Java class. Реализуем в нем интерфейс Scenario:

package test_scenario;
import swing.*;
import org.netbeans.jemmy.*;
import org.netbeans.jemmy.operators.*;
/**Testing class by Jemmy 2 toolkit
*
* @author Sokunova Mariya
*/

public class SwingApplicationScenario implements Scenario{
}

Этот класс должен переопределять метод runIt() . Внутри этого метода мы опишем тест, реализующий поиск всех нужных компонентов, нажатие кнопок, ввод текста и т.д. Получим метод следующего вида:

public int runIt(Object param) {
try {
//Запуск Swing приложения
new ClassReference("swing.SwingApplication").startApplication();
//Поиск JFrame
JFrameOperator mainFrame = new JFrameOperator("SwingApplication");
//Делаем небольшую задержку
new QueueTool().waitEmpty(200);
//Ищем кнопку по имени
JButtonOperator firstButton = new JButtonOperator(mainFrame, "First");
firstButton.push();
//Ищем JTextField по имени после нажатия кнопки должно стать "First pressed"
JTextFieldOperator textField = new JTextFieldOperator(mainFrame,"First pressed");
//Очищаем поле ввода
textField.clearText();
textField.enterText("Hello");
//После ввода текста на JFrame должен появиться JLabel
//Метка должна быть "Hello"

new JLabelOperator(mainFrame,"Hello");
//Поиск второго компонента JTextField по индексу
JTextFieldOperator textField2 = new JTextFieldOperator(mainFrame,1);
textField2.enterText("Field2");
//Опять ищем JLabel по тексту
new JLabelOperator(mainFrame,"Field2");
JButtonOperator secondButton = new JButtonOperator(mainFrame, "Second");
secondButton.push();
mainFrame.close();
}
catch(Exception e) {
e.printStackTrace();
return(1);
}
return(0);
}

Для запуска теста делаем класс исполняемым. Создаем метод main:

public static void main(String [] argv){
String[] params = {"test_scenario.SwingApplicationScenario"};
org.netbeans.jemmy.Test.main(params);
}

Полный код класса SwingApplicationScenario.java
Компилируем класс:
# javac /home/JavaClasses/test_scenario/SwingApplicationScenario.java

Запускаем тест из командной строки:
# java test_scenario.SwingApplicationScenario

Наименование окон, кнопок и других компонентов можно задавать не только напрямую в тексте программы, но и считывать значения из файла. Для этого используем объект класса Bundle и его метод getResource():

...
Bundle bundle = new Bundle();
bundle.loadFromFile("/home/java/properties_for_test");
//wait frame
JFrameOperator mainFrame = new JFrameOperator(bundle.getResource("main_window"));
...

Пример файла с заданными параметрами: properties_for_test.
Класс использующий передачу параметров из файла: ScenarioByFile.java.

Тестирование с помощью JUnit4 framework

Внешние (public) методы, а также API нужно обязательно тестировать с помощью Unit-тестов. В нашем случае мы напишем Unit-тесты для тестирования GUI.
Чем меньше будет Unit-тест, тем проще его будет отлаживать. Название каждого Unit-теста сделаем подробным и читаемым. Не скупитесь на комментарии.
Основные ключевые слова(обозначения) в Unit-тестах следующие:

  • @Test - Метод, перед которым идет ключевое слово @Test - является тестом
  • @Ignore - Пропуск теста ( этот метод не будет выполняться)
  • @Before - Метод, перед которым идет ключевое слово @Before выполняется перед каждым методом @Test
  • @After - Метод, перед которым идет ключевое слово @After выполняется после каждого методо @Test
  • @BeforeClass - Метод, перед которым идет ключевое слово @BeforeClass выполняется один раз перед запуском всех тестов
  • @AfterClass - Метод, перед которым идет ключевое слово @AfterClass выполняется один раз при окончании всего тестирования

Методы обозначенные как @Before/@After должны быть public. Методы обозначенные как @BeforeClass/@AfterClass должны быть public static.

Каждый Unit-тест должен иметь утверждающие методы, такие как:

  • assertEquals("message",A,B) - для проверки совпадения значений простых типов, а для объектов вызывается метод equals класса этих объектов. Если equals() не определен, то assertEquals() работает, как assertSame().
  • assertSame("message",A,B) - для проверки совпадения значений простых типов, а для объектов совпадения ссылок (должны указывать на один и тот же объект).
  • assertTrue("message",A) - проверяем, что условие верно.
  • assertNotNull("message",A) - проверяем, что объект создался.

И многие другие.

Теперь обернем все пункты из предыдущих тестовых сценариев в небольшие, независимые тесты. В блок @Before завернем создание тестового приложения:

@Before
public void getFrame(){
//Создаем приложение
SwingApplication.startApp();
//Ищем фрейм
mainFrame=new JFrameOperator();
//Это приостановление очереди выполнения событий
//просто для того, чтобы было удобнее смотреть

mainQueue = new QueueTool();
mainQueue.waitEmpty(200);
}

В блок @BeforeClass обернем временные настройки - ожидание окна, ожидание компонентов и т.д. Если за указанные промежутки времени компоненты не будут найдены, то тест будет провален.

@BeforeClass
public static void setTimeouts(){
//Скорость набора символов
JemmyProperties.setCurrentTimeout("JTextComponentOperator.PushKeyTimeout", 50);
//Максимальное время ожидания JFrame
JemmyProperties.setCurrentTimeout("FrameWaiter.WaitFrameTimeout", 2000);
//Максимальное время ожидания всех компонентов
JemmyProperties.setCurrentTimeout("ComponentOperator.WaitComponentTimeout", 1000);
}

Теперь напишем простой тест, определяющий корректность названия нашего окна JFrame:

@Test
public void testTitleFrame(){
//У найденного ранее фрейма берем параметр Имя окна
String titleFrame = mainFrame.getTitle();
//Сравниваем с ожидаемым значением
assertEquals("SwingApplication",titleFrame);
mainQueue.waitEmpty(100);
}

Аналогично обернем в тест нажатие кнопок и проверку корректности значений в полях. Как было видно из п.4 ( разработки Swing приложения) при нажатии на кнопку First в поле myTextField должен был появиться текст "First pressed". Это мы и будем использовать, как проверочное условие.

@Test
public void testActionButtons(){
//Теперь находим кнопку по её имени First
JButtonOperator butn1 = new JButtonOperator(mainFrame,"First");
//Осуществляем клик
butn1.push();
JTextFieldOperator LeftField = new JTextFieldOperator(mainFrame,0);
//Сравниваем значение поля с ожидаемым
assertTrue("After First Button push LeftField should " +
"contained \"First pressed\" string",LeftField.getText().equals("First pressed"));
mainQueue.waitEmpty(500);
JButtonOperator butn2 = new JButtonOperator(mainFrame,"Second");
butn2.push();
JTextFieldOperator RightField = new JTextFieldOperator(mainFrame,1);
//Делаем проверку на НЕ совпадение значений
assertFalse("After Second Button push RightField should " +
"not contain \"AAA\" string ",RightField.getText().equals("AAA"));
mainQueue.waitEmpty(500);
}

Добавим аналогичный тест для проверки реакции на ввод текста в поля и обновления JLabel. А также тест, который смотрит все ли компоненты присутствуют. Полный исходный код тестового класса TestSwingApplication.java
Запускаем тестовый класс в IDE Netbeans 6.5:

Компилируем тест( должен лежать в той же папке что и тестируемое приложение):
# javac /home/JavaClasses/swing/TestSwingApplication.java

Запускаем:
# java org.junit.runner.JUnitCore swing.TestSwingApplication

Результат:
...
Time: 10.567
OK (4 tests)

Замечание: В новой версии JUnit 4 Вам не надо делать тестовые классы наследниками Test Case, как было в версии JUnit 3.

Исходный код проекта Swing с использованием Jemmy ver.2 для тестирования GUI : Swing_UnitTests.tar.gz.
jar файл классов для выполнения примеров: Swing_UnitTests.jar.
Тестовый файл настроек: properties_for_test.
Более подробно о Jemmy Toolkit можно прочитать здесь.

Обновлено 11.02.2011 19:03
 
Алгоритм сортировки прямым выбором PDF Печать E-mail
Автор: Mariya Sokunova   
30.10.2010 16:48

Сортировка массива методом прямого выбора на Java

Описание метода

Описание взято из книги "Алгоритмы и Струкуры данных" Никлаус Вирт :
Этот прием основан на следующих правилах:

  • Выбирается элемент с наименьшим ключом.
  • Он меняется местами с первым элементом a1
  • Затем этот процесс повторяется с оставшимися n-1 элементами, n-2 элементами и т.д. до тех пор, поне не останется один, самый большой элемент.

Анализ прямого выбора. Число сравнений ключей (С), очевидно, не зависит от начального порядка ключей. Для С имеем:
C=(n²-n)/2

 

Краткое описание алгоритма

На вход передается массив из целых чисел. В функции sortArray за минимальный элемент берется первый элемент массива. И далее этот элемент сравнивается со всеми идущими после него элементами. Если элемент меньше, чем текущий минимальный, то элементы массива меняются местами. Таким образом на первом месте ( с индексом 0) становится наименьший элемент из всего массива. Далее за минимальный берется второй элемент массива и опять сравнивается со всеми оставшимися и т.д.

Описание входных данных

В выполняемом файле MainClass.java в статическую функцию sortArray класса Sorting передается массив целых чисел. Этот массив сортируется по-возрастанию. Так как каждый массив является ссылкой, создавать дополнительную копию данных не требуется. Элементы меняются напрямую в исходном массиве.

Текст программы с комментариями:

Класс Sorting содержит методы для сортировки массива целых чисел.

/**Класс для сортировки целочисленных массивов
* Реализует алгоритм прямого выбора
* @author Sokunova
* @version 1.1 27.10.2010
*/

public class Sorting {
/**Метод сортировки массива по возрастанию
* @param array массив для сортировки
*
*/

static void sortArray(int[] array){

for (int i=0;i<(array.length-1);i++){
int minimumItem = array[i];
//Сравниваем элементы по индексу i и i+1, если второй меньше, то меняем их местами
checkItemsWithMinimum(minimumItem,i,i+1,array);

}
}
/**вспомогательный метод, для сравнения и перестановки элементов массива местами
*/

private static void checkItemsWithMinimum(int minimum,int minindex,int index,int[] array){
for(int i=index;i<array.length;i++){
if (minimum > array[i]){
array[minindex]=array[i];
array[i]=minimum;
minimum=array[minindex];

}
}
}
/**Метод позволяющий вывести все элементы массива
*
* @param array массив для вывода
*/

static void printArray(int[] array){
System.out.println("printArray");
for(int i=0;i<array.length;i++){
System.out.println("i = "+ i+" array[i] = "+array[i]);
}
}
}

Пример вызова класса Sorting из выполняемого класса MainClass.

/**Тестовый класс для вызова метода сортировки
*
* @author Sokunova
*/

public class MainClass {

/**
* @param args the command line arguments
*/

public static void main(String[] args) {
// Тестовый массив целых чисел
int[] testArray = new int[] {-5,1,17,33,1,355,7,88,90,12,3,4,0,12};
Sorting.sortArray(testArray);
Sorting.printArray(testArray);

}
}

Исходный текст программы: Sorting.java.
Пример выполняемого класса: MainClass.java.

Обновлено 31.10.2010 08:11
 



При использовании материалов сайта ссылка на сайт с указанием авторов обязательна.
Designed by Joomla. Powered by Mikhail Shpatserman & Mariya Sokunova © 2009-2011