First we need something to test. Therefor I put together a little example application to demonstrate the UI testing approach.

First we need something to test. Therefor I put together a little example application to demonstrate the UI testing approach.

−

Just create a new plugin project, add <code>org.eclipse.rap.ui.workbench</code> as a dependency and create the following class:

+

Just create a new plugin project, add <code>org.eclipse.rap.ui.workbench</code> as a dependency, also add all the needed dependency to avoid errors, and create the following class:

<pre>

<pre>

package org.eclipse.rap.demo.ui.tests;

package org.eclipse.rap.demo.ui.tests;

Line 47:

Line 47:

MessageDialog.openInformation(s,

MessageDialog.openInformation(s,

"MessageBox",

"MessageBox",

−

"Changing the button text now...", null);

+

"Changing the button text now...");

b1.setText("After");

b1.setText("After");

}

}

Line 54:

Line 54:

s.pack();

s.pack();

s.open();

s.open();

+

while (!s.isDisposed()) {

+

if (!d.readAndDispatch())

+

d.sleep();

+

}

return 0;

return 0;

}

}

Line 60:

Line 64:

</pre>

</pre>

−

As you can see, this is a really tiny RAP example - but big enough to be worth to test it.

+

WARNING: this code is not complete, it's a snippet, you will have to realize the Activator class and entry point configuration.

+

As you can see, this is a really tiny RAP example - but big enough to be worth to test it.

+

At the moment, all widget ids are looking like "w1", "w2", "w3". As these ids are generated in the order the widgets are created, it isn't a very good way to handle it. Imagine: You wrote hundred of testcases and then you see: Ah, my application needs an additional label somewhere, all the widgets created after this label have another ID.

At the moment, all widget ids are looking like "w1", "w2", "w3". As these ids are generated in the order the widgets are created, it isn't a very good way to handle it. Imagine: You wrote hundred of testcases and then you see: Ah, my application needs an additional label somewhere, all the widgets created after this label have another ID.

Line 69:

Line 75:

==Preparing the tests==

==Preparing the tests==

−

Now create a new java project and add the JUnit library. Additionally, you need to add the <code>selenium-java-client-driver.jar</code> to your project in order to use the Selenium Remote Control.

+

Now create a new '''java project''' and add the JUnit library. Additionally, you need to add the <code>selenium-java-version.jar</code> (e.g: selenium-java-2.3.0.jar ) and needed libs to your project in order to use the Selenium Remote Control.

As RAP applications have a little bit other nature than normal web applications, we need to work around some techniques of selenium. First, you can use commands like <code>click</code> against a target element on your page. The target defined as in <code>id</code> element of your (x)html source. As Qooxdoo doesn't provide ids for their widgets, we need the mentioned [http://wiki.openqa.org/display/SEL/qooxdooExtension Selenium User Extension for Qooxdoo]. With this, all targets prefixed with "qx=" will now use another "find-the-target" algorithm based on <code>UserData</code> provided by Qooxdoo widgets.

As RAP applications have a little bit other nature than normal web applications, we need to work around some techniques of selenium. First, you can use commands like <code>click</code> against a target element on your page. The target defined as in <code>id</code> element of your (x)html source. As Qooxdoo doesn't provide ids for their widgets, we need the mentioned [http://wiki.openqa.org/display/SEL/qooxdooExtension Selenium User Extension for Qooxdoo]. With this, all targets prefixed with "qx=" will now use another "find-the-target" algorithm based on <code>UserData</code> provided by Qooxdoo widgets.

Line 127:

Line 133:

Now we are ready to take off and write the first testcase...

Now we are ready to take off and write the first testcase...

+

(Test case rewrited for jUnit4, feel free to implement it with older version of Junit (without annotations).

==First RAP UI Testcase==

==First RAP UI Testcase==

−

<pre>import junit.framework.TestCase;

+

<pre>package testapp;

−

public class AppTest extends TestCase {

+

import org.junit.After;

+

import org.junit.AfterClass;

+

import org.junit.Assert;

+

import org.junit.Before;

+

import org.junit.BeforeClass;

+

import org.junit.Test;

+

+

public class RapTestCase {

private RAPSelenium sel;

private RAPSelenium sel;

Line 137:

Line 151:

private static final String BUTTON = "myButton";

private static final String BUTTON = "myButton";

−

protected void setUp() throws Exception {

+

@Before

+

public void setUp() throws Exception {

sel = new RAPSelenium("localhost", 4444,

sel = new RAPSelenium("localhost", 4444,

−

"*firefox /usr/lib/firefox/firefox-bin",

+

"*firefox C:/Program Files/Mozilla Firefox 4.0 Beta 12/firefox.exe",

−

"http://localhost:8080/rap");

+

"http://localhost:8080/rap"); //change to working path to you firefox and test application.

sel.start();

sel.start();

}

}

+

@Test

public void testButton() {

public void testButton() {

−

sel.open("http://localhost:8080/rap?startup=foo");

+

sel.open("http://127.0.0.1:10080/rap?startup=TestApp.entrypoint1"); //change it to feet your configuration (url, name and port).

sel.waitForElementPresent(BUTTON);

sel.waitForElementPresent(BUTTON);

// checking button

// checking button

−

assertEquals("Before", sel.getText(BUTTON));

+

Assert.assertEquals("Before", sel.getText(BUTTON));

// checking message dialog

// checking message dialog

sel.clickAndWait(BUTTON);

sel.clickAndWait(BUTTON);

+

// // Close the popup to view go back to the button

+

sel.clickAndWait("w10"); // was w10 for me, check the Id of yours. You can alternativly set a specific id for the popup validation button.

// check button afterwards

// check button afterwards

−

assertEquals("After", sel.getText(BUTTON));

+

Assert.assertEquals("After", sel.getText(BUTTON));

}

}

−

protected void tearDown() throws Exception {

+

@After

+

public void AfterClass() throws Exception {

sel.stop();

sel.stop();

}

}

−

−

}

</pre>

</pre>

Line 179:

Line 196:

INFO: Started org.mortbay.jetty.Server@a62fc3

INFO: Started org.mortbay.jetty.Server@a62fc3

</pre>

</pre>

−

If you don't get an output like this in the last lines, be sure you have access to the port 4444 or change it with the commandline paramter of the selenium server. See [http://openqa.org/selenium-rc/options.html Command Line Options]

+

If you don't get an output like this in the last lines, be sure you have access to the port 4444 or change it with the commandline paramter of the selenium server.

+

See [http://openqa.org/selenium-rc/options.html Command Line Options], you will perhaps also need to rename qooxdoo-user-extension.js to user-extension.js.

THIS IS EXPERIMENTAL! WE MAKE OUR RESEARCH RESULTS AVAILABLE TO ENABLE FEEDBACK

Writing UI Tests for RAP applications

This document will describe one possible way to do UI tests for RAP applications. With this approach we use the combination of JUnit, Selenium and Selenium RC to have automated UI tests which can be integrated into your JUnit testsuite.

Below is a simple example application to demonstrate the usage of the tool chain.

Requirements

If you don't have it, you need at least these files in order to follow the tutorial:

WARNING: this code is not complete, it's a snippet, you will have to realize the Activator class and entry point configuration.
As you can see, this is a really tiny RAP example - but big enough to be worth to test it.

At the moment, all widget ids are looking like "w1", "w2", "w3". As these ids are generated in the order the widgets are created, it isn't a very good way to handle it. Imagine: You wrote hundred of testcases and then you see: Ah, my application needs an additional label somewhere, all the widgets created after this label have another ID.
That's why we use WidgetUtil.CUSTOM_WIDGET_ID as key for custom widget data. This way we can easily set a human-readable name for each of the widgets under test.

Starting the app

Besides adding the extension for the new entrypoint is it registered there is one important thing to do. We need to activate the UI Test mode in order to get several advantages during running the tests. Just add -Dorg.eclipse.rwt.enableUITests=true as VM argument to your launch configuration.

Preparing the tests

Now create a new java project and add the JUnit library. Additionally, you need to add the selenium-java-version.jar (e.g: selenium-java-2.3.0.jar ) and needed libs to your project in order to use the Selenium Remote Control.

As RAP applications have a little bit other nature than normal web applications, we need to work around some techniques of selenium. First, you can use commands like click against a target element on your page. The target defined as in id element of your (x)html source. As Qooxdoo doesn't provide ids for their widgets, we need the mentioned Selenium User Extension for Qooxdoo. With this, all targets prefixed with "qx=" will now use another "find-the-target" algorithm based on UserData provided by Qooxdoo widgets.

To not prefix all your IDs with the "qx=" special locator, copy this class to your test project to use it as your Selenium connector:

This class is a simple wrapper around the existing Selenium class which helps you with some sort of problems.
Be careful: As it's extends the base Selenium class, you're able to use other methods of the original class which are not overridden by the RAPSelenium class. If you forget to add the "id=" locator yourself, you'll not get any useful return result.

Now we are ready to take off and write the first testcase...
(Test case rewrited for jUnit4, feel free to implement it with older version of Junit (without annotations).

Running the Selenium RC server

The Selenium RC server is a little server written in Java which cares about the interaction between your JUnit tests and the browser instances. As it offers some webservices for us, we need to start it before running our tests.

If you don't get an output like this in the last lines, be sure you have access to the port 4444 or change it with the commandline paramter of the selenium server.
See Command Line Options, you will perhaps also need to rename qooxdoo-user-extension.js to user-extension.js.

Execution

start your sample application

start selenium RC server

run the test case :)

troubleshootings :

check path to your RAP application and firefox exec.

check dependancies

check your rap application entrypoint and activator class.

And now?

As you see, all that stuff is really hacky and should be used carefully. As this was the first try to combine RAP applications and UI Tests, there is much work to do in this area. When we have some time in the future, we will consider working on an own RAP User Extensions for Selenium with some improvements and also on a - at least - simple infrastructure for your UI tests.

AFAIK the Selenium "type" command is not yet working with qooxdoo textboxes. So be patient...

If you have great ideas or thoughts how we could improve the whole story, don't hesitate to contact us in the RAP newsgroup or add your thoughts to the Ideas section of this page.