FeaturesPluginsDocs & SupportCommunityPartners
Author: Alexandre Iline
Last modified: 21 December 2004

Writing Jelly Tests in NetBeans 3.6 Guide

Related modules: Jemmy | Test Tools | Jemmy Support | XTest

Table of Contents:


Introduction

This document describes how to write GUI tests for NetBeans IDE using Jelly library. Jelly contains classes to cover most of the functionality necessary to work with NetBeans IDE core GUI components and windows.

See Writing Operators Guide for instructions how to create classes covering non-core (i.e. module specific) NetBeans GUI parts.

Jelly is based on Jemmy library. Jelly provides direct access to Jemmy functionality, so you can use Jemmy in your tests directly. (See more info about Jemmy using below). However, it'd be highly recommended to put as much functionality as possible into module specific operators.

During this document we will create test (WholeTest.java) which

  • creates a Java class - HelloWorld.java under <netbeans.user>/sampledir directory
  • types main(String[]) method into it
  • executes it
  • checks compilation status and output

Parts of this test used in tutorial below as examples. Any of these parts is kept in separate test which can be executed alone. Some of them require HelloWorld to exist, another create it - this all mentioned in each test comments.


How to execute test

During the tutorial you migh want to execute the tests described above.

Steps to do it:

  • Launch NetBeans
  • Mount <netbeans home>/sampledir directory
  • Mount jars: jemmy.jar, jelly2-nb.jar, junit.jar, junit-ext.jar. For other tests you might also need jars like core.jar, openide.jar...
  • Mount directory containing org/netbeans/jellytools/examples/*.java
  • Make sure there is no HelloWorld exists (or does exist, depending on test comments) in <netbeans home>/sampledir filesystem.
  • Select a test you are about to execute.
  • Change its "Executor" property to "Internal Executor"
  • Execute it.


How to create test

Test case should extend JellyTestCase.
Tests in Jelly are supposed to be executed in XTest harness which gives a possibility to have test as NbTestCase(TestCase) subclass, however JellyTestCase is strongly recommended as test superlass, because it performs correct Jemmy initialization and handles test case exceptions.

Example:

package org.netbeans.jellytools.examples;

import org.netbeans.jellytools.JellyTestCase;

import org.netbeans.junit.NbTestSuite;

//does nothing
//it's just an example how test could look.
public class EmptyTest extends JellyTestCase {
    
    //constructor required by JUnit
    public EmptyTest(java.lang.String testName) {
        super(testName);
    }

    //method allowing to execute test directly
    //from IDE for debugging    
    public static void main(java.lang.String[] args) {
        junit.textui.TestRunner.run(suite());
    }
    
    //method required by JUnit
    public static junit.framework.Test suite() {
        return new NbTestSuite(EmptyTest.class);
    }
 
    //method to define test scenario
    public void testScenario() {
        //test scenario
    }
}
This test does not really do anything - working code should be in testScenario() method. Pay attention, though, to the main(String[]) method. It's optional, however, it's good idea to have this method - it allows to debug the test directly from IDE (see above).

You can use a template to create a test.

You can also take a look on Jelly internal tests.
 

JellyTestCase

JellyTestCase is an extention of NbTestCase intended for Netbeans GUI testing explicitly.

JellyTestCase initialises Jemmy. Contents of that initialization can be changed any time. As for now, it initialises output so test output is really going to log provided by NbTestCase and set some timeouts necessary for NetBeans GUI testing.

Any JemmyException (which is normally thrown as a result of unsuccessful operation in Jemmy) going from a test is treated by JellyTestCase as a test failure, any other exception - as a test error.

Using its public switches you can define whether test should

  • close all modal dialog at the end of test case (switch jemmy.close.modal - default true)
  • generate component dump (XML file containing components information) in case of test failure (switch jemmy.screen.xmldump - default false)
  • capture screen into PNG file in case of test failure (switch jemmy.screen.capture - default true)
  • wait at least 1000 ms between test cases (switch jelly.wait.no.event - default true)



What to use

Jelly classes could be divided on four structural parts:

Operators

All Jelly operators are subclasses of Jemmy operators. Jemmy operators is a set of classes which are test-side agents for application components. Operators provide all possible methods simulating user action with components, methods to find and wait components and windows. Also operators map all components methods through the event queue used for event dispatching.

However, there are some specific operators which are written to cover specific NetBeans IDE GUI features. Some of these operators are described in Low level Jelly operators section of this tutorial.

Jelly provides all high level operators necessary for NetBeans core GUI.

All of the operators provide access to their subcomponents by "getters" methods. These methods are implemented in "lazy initialization" technique, so real suboperator instances are not initialized until it's necessary. All of the suboperators are initialized by verify() method invocation, so this method using guarantee that all subcomponents are already loaded.

Getters method names usually has prefix showing component type, the rest of the names usually repeats component text or a text of corresponded label: JButtonOperator btCancel(), JRadioButtonOperator rbSubString(), JTextFieldOperator txtName(). You can see a full list of prefixes in a table showed in Writing Operators Guide.

Some of the operators hase invoke() method which causes component displaying using an action. You can use these actions directly - their names are usually look like *ViewAction (RuntimeViewAction for RuntimeTabOperator).
 

Actions

Action classes mission is to replace menu and popup calls.

Most of the actions inside NetBeans IDE could be done in some different modes. org.netbeans.jellytools.actions.Action class designed to describe all the modes action could be performed:

  • by menu
  • by popup
  • by shortcut
  • by direct API call

Not all of the actions can be performed by all modes above, however, most of the menu/popup operations in Netbeans can also be done by shortcuts and direct API calls. Keeping that in mind, try to use propertly created Action instance whenever you want to perform menu or popup operations. Even if you do care to perform operation exactly by menu (popup), create an action. The very same code could be used in different place in another mode.

Using perform() method of any action you will perform action by the first available possible mode it could be performed. Sequence of modes to be tried is defined by Action.setDefaultMode(int) value.

Example (from TestShowFilesystems.java):

        //create an action which knows how to show Filesystems
FilesystemsViewAction viewAction = new FilesystemsViewAction();
//show Filesystems by main menu
viewAction.perform();
Example above is performed in default mode. Knowing that explorer can also be showed by direct API call we can also use performMenu() and performAPI() methods directly.

Example (from TestShowFilesystems.java):

        //show Filesystems by main menu
viewAction.performMenu();
//show Filesystems by API call
viewAction.performAPI();
Actions can also be performed for node, array of nodes, and component. If action is performed for a node (nodes), node is selected first.

Action classes are in org.netbeans.jellytools.actions package and module specific org.netbeans.jellytools.modules.<modulename>.actions packages.

Nodes

Node objects are designed to make tree operations easier. Node incapsulates information about tree it's displayed in and about tree path. Node classes can be used to perform applicable operations to the nodes in Explorer window. Nodes can also be passed into Action.perform*(Node) methods.

There are some useful node types defined in Jelly, such as: ClassNode, FilesystemNode, FolderNode, HTMLNode, ProjectRootNode, RepositoryRootNode.

Example (from TestNodes.java):

        //create a node for the sampledir directory
FilesystemNode tmpNode = new FilesystemNode(
System.getProperty("netbeans.user") +
File.separator +
"sampledir");
//create a node for the source
JavaNode clNode = new JavaNode(tmpNode, "HelloWorld");
//select it
clNode.select();
//start text editing
clNode.open();
//delete node
clNode.delete();
Nodes classes are in org.netbeans.jellytools.nodes package andorg.netbeans.jellytools.modules.<modulename>.nodes packages.

Properties

org.netbeans.jellytools.properties package contains classes to work with properties. Besides PropertySheetOperator it contains classes - inheritors of org.netbeans.jellytools.properties.Property class.

Property value can be changed by inline editors (text field for string values, check box for boolean values - setValue(String) method and combo box for list values - setValue(int) method.

Example (from TestProperties.java):

        //find "Encoding" property
Property encodingProperty = new Property(propertiesSheet, "Encoding");
//change value directly
encodingProperty.setValue("iso-8859-1");

Any property may or may not have "..." button invoking a property editor. If property has this button, it can have any number of set*Value(*) methods using the editor for property editing.

Some property might be not editable directly (other than by property editor). They extend Property and override setValue(*) methods to use property editor. But if property is editable directly, which most of them are, setValue(*) methods work directly.

Property editors are another class of operators which are located inside org.netbeans.jellytools.properties.editors package and org.netbeans.jellytools.modules.<modulename>properties.editors packages.

Examples of property editors are: ColorCustomEditorOperator, DimensionCustomEditorOperator, FileCustomEditorOperator, PointCustomEditorOperator, StringCustomEditorOperator.

Property editor can be invoked by Property.openEditor() method.

Example (from TestProperties.java):

        //find "Encoding" property
Property encodingProperty = new Property(propertiesSheet, "Encoding");
//show property editor
encodingProperty.openEditor();
//create an operator for the property editor
StringCustomEditorOperator encodingEditor = new StringCustomEditorOperator("Encoding");
//set a different value
encodingEditor.setStringValue("iso-8859-2");
//push OK
encodingEditor.ok();

Wizards

There are some operators in Jelly providing functionality covering NetBeans wizards: WizardOperator, NewWizardOperator, ChooseTemplateStepOperator, TargetLocationStepOperator.

Example (from TestWizard.java):

        //invoke wizard on the node, select template and go next
        NewWizardOperator wizard = NewWizardOperator.invoke(tmpNode, "Java Classes|Class");
        //type name
new NewObjectNameStepOperator().setName("HelloWorld");
        //push finish
        wizard.finish();
Example (from TestMainWindow.java):
        //create an operator for the wizard
        NewWizardOperator wizard = new NewWizardOperator();
        //select template
        new ChooseTemplateStepOperator().selectTemplate("Java Classes|Class");
        //push "Next >" button
        wizard.next();
        //type name
        new TargetLocationStepOperator().setName("HelloWorld");
        //push "Finish" button
        wizard.finish();

Low level Jelly operators

This section contains description of some most important low-levels jelly operators which are designed to use all NetBeans GUI components functionality for testing. They also hide some specific details from user.
 

NbFrameOperator

NbFrameOperator hides the difference between MDI and SDI mode. It's common ancestor for all Jelly frames operator except MainWindowOperator. A test using frame operators based on this class is able to work in both modes.
 

NbDialogOperator

Like NbFrameOperator, NbDialogOperator is common superclass for all dialog operators in Jelly. It has some common methods pushing most used buttons on dialog: "OK", "Cancel", "Close", "Help".
 

TopComponentOperator

TopComponentOperator is an operator for org.openide.windows.TopComponent component. It provides shortcuts to docking operations.


High level Jelly operators

MainWindowOperator

MainWindowOperator represents main NetBeans IDE window. The operator has shorcuts to workspaces tabs and toolbar.

The operator does not have any menu shortcuts because in Jelly menu operations supposed to be done by actions.

Example (from TestMainWindow.java):

        //get a reference to main window
        MainWindowOperator mainWindow = MainWindowOperator.getDefault();
        //push "New" toolbar button to invoke a wizard
        mainWindow.getToolbarButton(mainWindow.getToolbar("System"), "New").push();

OutputWindowOperator

The operator (together with TermOperator) provides access to output window components. Their methods allow to work with output pages and text.

Example (from TestOutput.java):

        //create an operator for output window
        OutputWindowOperator output = OutputWindowOperator.invoke();
        //select "HelloWorld" page and get a terminal from it
        //wait "Hello, world!" to be displayed
        output.getTerm("HelloWorld").waitText("Hello, world!");
        //select "Compiler" page
        output.selectCompilerPage();
        //check that "Finished" text displayed
        if(output.getText().indexOf("Finished ") == -1) {
            fail();
        }

ExplorerOperator

Most of the functionality to work with Explorer window is in nodes, so the only support necessary from ExplorerOperator is switching pages and and tree containers searching. Example (from TestExplorer.java):
        //show explorer
        ExplorerOperator explorer = ExplorerOperator.invoke();
        //select "Runtime" page
        explorer.selectPageRuntime();
        //select "Repository" page and get an operator from there
        RepositoryTabOperator repository = explorer.repositoryTab();
ecause the Explorer is only a logical group of top components, it is recommended to use operators of such components directly. For example:
        // opens Runtime tab
        RuntimeTabOperator.invoke();
        // finds Filesystems tab and returns its root node instance
        Node rootNode = new RepositoryTabOperator().getRootNode();

EditorOperator

EditorOperator has shortcuts for editing operations. It also gives reference to JEditorPaneOperator for a JEditorPane using for text editing.

Example (from TestEditor.java):

        //find editor window
        EditorWindowOperator editorWindow = new EditorWindowOperator("HelloWorld");
        //find editor
        EditorOperator editor = editorWindow.selectPage("HelloWorld");
        //get reference to editing component
        JEditorPaneOperator txtOper = editor.txtEditorPane();
        //put caret at the beginning of "public class HelloWorld" line
        txtOper.setCaretPosition(
            txtOper.getPositionByText("public class HelloWorld"));
        //move caret 1 line down
        editor.setCaretPositionToLine(editor.getLineNumber() + 1);
        //push Enter
        txtOper.pushKey(KeyEvent.VK_ENTER);
        //push up arrow
        editor.pushUpArrowKey();
        //pust Tab
        editor.pushTabKey();
        //type first line
        txtOper.typeText("public static void main(String[] argv) {\n");
        //type second line
        txtOper.typeText("System.out.println(\"Hello, world!\");\n");


WholeTest execution.

Let execute the test we finally have - WholeTest.java.

As was said above, tests are supposed to be executed by XTest harness, but let's run it from IDE directly. Just perform all the steps described above.

If everithing went right, at the end you should see that HelloWorld item removed and "HelloWorld - I/O" page in output window contains "Hello, world!" text.


Using bundles.

Moving most of functionality into operators makes tests live longer with no changes in tests itself, because most of the changes in tested code can be covered by operators changing. However, some of functionality still could be uncovered.

One of the places where tests are tied to tested code version is string resources. Again, most of the resources will be used in operators. But the rest of them should be gotten from NetBeans bundles, so even if a resource is changed for NetBeans, tests uses right resource value anyway.

It can be done by Bundle class using. Class Bundle is able to get resource value by package and resource names.

Example (from I18NTest.java):

        //invoke wizard on the node
        NewWizardOperator wizard = NewWizardOperator.invoke(tmpNode, "Java Classes|" +
            Bundle.getString("org.openide.src.nodes.Bundle", "CTL_Class"));
        ...
        //wait for a status in main window footer
        MainWindowOperator.getDefault().waitStatusText(
            Bundle.getStringTrimmed("org.netbeans.core.compiler.Bundle", "MSG_CompilationSuccessful"));
Using Bundle class automatically makes possible of I18N application testing, because localized resource values will be used instead.

Using Jemmy in tests

Classes provided by Jelly have most methods necessary to perform operations in "usual" way. So, if you do not need to do something "unusual", methods you need are probably there already.

As was mentioned above Jelly operators extend JemmyOperators, so you can use them as regular jemmy operators. Additionly, most of the Jelly operators have shortcuts to get operators for most of their subcomponents. These operators are, again, either Jemmy operators or Jelly operators. So there is no need to create any operator yourself.




Companion
Projects:
MySQL Database Server   Open JDK: an Open SourceJDK   GlassFish Community: an Open Source Application Server    Mobile & Embedded Community    Open Solaris   java.net - The Source for Java Technology Collaboration   Virtual Box - full virtualizer  Open ESB - The Open Enterprise Service Bus Powered by