Тестирование 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 можно прочитать здесь. |