-
Notifications
You must be signed in to change notification settings - Fork 0
Getting started
Since gwt-test-utils is a testing framework, this page won't explain how to develop a "Hello World !" application, but how to test a GWT "Hello world!" application instead :-)\ Therefore, the purpose of this page is to help you setting up gwt-test-utils basics and writing your first test in under 5 minutes.
If you're not familiar with GWT yet... well, it would be hard to have a GWT app and some unit tests working in 5 minutes! You should consider the GWT official Getting Started before continuing with gwt-test-utils.
Let's create an out-of-the-box GWT application with the GWT plugin for eclipse :
-
Select File > New > Web Application Project from the Eclipse menu.
-
In the New Web Application Project wizard, enter a name for your project : GwtTestSample and a java package name : com.sample.mywebapp.
-
Be sure Generate GWT sample code is checked
-
Click Finish.
Launch the generated 'GwtTestSample' application. It is very basic :
- one label
- one textbox
- one 'Send' button
Fill the textbox with 'World', and click the button. The client calls the server which validate the text has at least 4 characters and fill a DialogBox with server-side generated HTML 'Hello, World!'. This is a typical GWT use case.
Ok, you now have a very simple "Hello World!" application. The most logical way to test it would be something like:
- Arrange: Fill the textbox
- Act: Click the 'Send' button
- Assert: Check the dialogbox is shown and its html is as expected
But testing it with GWTTestCase
would not be so simple. You would have to :
- Launch a jetty instance. Ok, this is done behind the scene by
GWTTestCase
, but it's very, very slow... - Design your unit test to be compatible with the asynchronous server call, e.g., tell
GWTTestCase
to wait x milliseconds for server response.. - Manually cleanup the DOM after each
@Test
method by overriding the gwtTearDown() method.
- Add all librairies required by gwt-test-utils :
- gwt-test-utils-X.jar
- javassist-3.12.1.GA+.jar
- junit-4.5+.jar
- slf4j-api-1.6+.jar
- fest-assert-core-2.0M7+.jar
- fest-util-1.2.2+.jar
- Create a META-INF/gwt-test-utils.properties file in your test classpath, e.g., in "test" directory. Add this line in it :
com.sample.mywebapp.GwtTestSample = gwt-module
You have to declare all your modules this way to let gwt-test-utils know which classes it will have to operate bytecode transformations on.
Note that you won't have to declare inherited modules.
In your sample application, it would be useless to declare com.google.gwt.user.User = gwt-module
since you have the corresponding inherits declaration in your GwtTestSample.gwt.xml.
At the end of GwtTestSample.onModuleLoad
method, add an HTML id to each widget you'll need easy access to:
public class GwtTestSample implements EntryPoint {
public void onModuleLoad() {
...
sendButton.getElement().setId("sendButton");
nameField.getElement().setId("nameField");
errorLabel.getElement().setId("errorLabel");
dialogBox.getElement().setId("dialogBox");
textToServerLabel.getElement().setId("textToServerLabel");
serverResponseLabel.getElement().setId("serverResponseLabel");
}
}
This is absolutely not mandatory, you could retrieve each widget directly from the RootPanel instance where it is registered. For Example : TextBox nameField = (TextBox) RootPanel.get("nameFieldContainer").getWidget(0);
. But it would not be very flexible: your test would need to know your exact DOM structure. Changing it would break your test.
In real projects, you also could set debug ids instead not to pollute your production's code with real ids.
Now, you can start writing your first test class, in the com.sample.mywebapp.client
package:
import static com.googlecode.gwt.test.assertions.GwtAssertions.assertThat;
import static com.googlecode.gwt.test.finder.GwtFinder.object;
@GwtModule("com.sample.mywebapp.GwtTestSample")
public class GwtTestSampleTest extends GwtTest {
private GwtTestSample app;
@Before
public void before() {
app = new GwtTestSample();
app.onModuleLoad();
// Some pre-assertions
assertThat(dialogBox()).isNotShowing();
assertThat(errorLabel()).isVisible().textEquals("");
}
private DialogBox dialogBox() {
return object("dialogBox").ofType(DialogBox.class);
}
private Label errorLabel() {
return object("errorLabel").ofType(Label.class);
}
}
-
The test class must extend
GwtTest
, which is the gwt-test-utils alternative to GWTGWTTestCase
. -
You also must annotate your test class with
GwtModule
to tell gwt-test-utils which module is under test. Be carefull to use a module you've declared in yourMETA-INF/gwt-test-utils.properties
with the 'gwt-module' key/value pair. Otherwise, an exception would be thrown. -
Before each test method, create and initialize a new app by calling the
EntryPoint.onModuleLoad()
method on it. The simple "pre assertions" demonstrate three important things: -
the DOM is automatically reinitialize between two tests :-)
-
You can use the
GwtFinder.object(...)
API to retrieve Widget instances very easily, based on their DOM id for example. -
You can use the
GwtAssertions.assertThat(...)
API to make fluent assertions on GWT widgets (it's based on the awesome fest-assert library).
Note: for a comfortable use of those APIs, we strongly recommend importing both GwtFinder
and GwtAssertions
utilities statically (How to do this easily in Eclipse).
Now it's time to write the unit test we were talking about:
@Test
public void clickOnSendMoreThan4chars() {
// Arrange: Fill the textbox
Browser.fillText(nameField(), "World");
// Act: Click the 'Send' button
Browser.click(sendButton());
// Assert: Check the dialogbox is shown...
assertThat(dialogBox()).isShowing().textEquals("Remote Procedure Call");
assertThat(errorLabel()).isVisible().textEquals("");
}
private TextBox nameField() {
return object("nameField").ofType(TextBox.class);
}
private Button sendButton() {
return object("sendButton").ofType(Button.class);
}
private HTML serverResponseLabel() {
return object("serverResponseLabel").ofType(HTML.class);
}
- This first unit test shows how to use the
Browser
API to simulate any DOM event of your choice.
Like for GwtFinder
and GwtAssertions
, you could add the Browser
class to your favorite static types. The choice is yours ;-)
DAMN IT'S RED :-(
Don't worry, the thrown GwtTestRpcException
was expected ;-) Have a look at the full error message:
"Illegal call to com.example.mywebapp.server.GreetingServiceImpl.getServletConfig() : You have to set a valid ServletMockProvider instance through GwtTestSamleTest.setServletMockProvider(..) method."
In GreetingServiceImpl.greetServer(...)
implementation, we have those two lines:
String serverInfo = getServletContext().getServerInfo();
String userAgent = getThreadLocalRequest().getHeader("User-Agent");
GreetingServiceImpl
, which is a javax.servlet.http.HttpServlet
implementation, relies on ServletConfig.getServletContext()
and HttpServletRequest
to retrieve serverInfo and userAgent data. Since gwt-test-utils doesn't emulate the entire servlet stack, you'll have to mock it at some point.
In some cases like above, your RemoteServices implementation will need access to the servlet API. When testing such code in a GwtTest
, mocking those access is mandatory and very easy to do.
gwt-test-utils provides a ServletMockProvider interface which will be used every time a RemoteServiceServlet
implementation calls
getThreadLocalRequest(), getThreadLocalResponse() or getServletConfig(). Custom implementations of this interface are setted using the protected setServletMockProvider
method available in your GwtTest
subclasses.
Here is you to setup a simple ServletMockProvider in your GwtTestSampleTest
class:
@Before
public void before() {
// use the provided adapter to implement only the methods you need for your test
setServletMockProvider(new ServletMockProviderAdapter() {
@Override
public ServletConfig getMockedConfig(AbstractRemoteServiceServlet remoteService) {
// mock the serverInfo in ServerConfig
MockServletContext context = new MockServletContext();
context.setServerInfo("mocked-server-info");
return new MockServletConfig(context);
}
@Override
public HttpServletRequest getMockedRequest(AbstractRemoteServiceServlet rpcService, Method rpcMethod) {
// mock the user-agent header in HttpServletRequest
MockHttpServletRequest mock = new MockHttpServletRequest();
mock.addHeader("User-Agent", "mocked-user-agent");
return mock;
}
});
...
}
With this configuration, you can safely expect that:
getServletContext().getServerInfo(); // always returns "mocked-server-info"
getThreadLocalRequest().getHeader("User-Agent"); // always returns "mocked-user-agent"
Note: gwt-test-utils comes with a set of mock implementations for most servlet API objects you may want to use to build your mocks: MockHttpServletRequest, MockHttpServletResponse, MockHttpSession, MockServletConfig, MockServletContext, MockRequestDispatcher.
Now that you have mocked serverInfo and userAgent data, you know exactly what should be displayed in the displayed popup. Change the assertions part of your unit test like this:
// Assert: Check the dialogbox is shown and its html is like expected
assertThat(dialogBox()).isShowing().textEquals("Remote Procedure Call");
assertThat(serverResponseLabel()).htmlEquals("Hello, World!<br><br>I am running mocked-server-info.<br><br>It looks like you are using:<br>mocked-user-agent");
assertThat(errorLabel()).isVisible().textEquals("");
It should been green now :-)
Now that you have our first green test, you may want write some others, because having a lot of green tests is cool! So, let's check an error is displayed when filling a less than 4 character String in the textbox:
@Test
public void clickOnSendLessThan4chars() {
// Arrange
Browser.fillText(nameField(), "123");
// Act
Browser.click(sendButton());
// Assert
assertThat(dialogBox()).isNotShowing();
assertThat(errorLabel()).isVisible().textEquals("Please enter at least four characters");
}
Very simple, isn't it ?
You may have noticed that the generated sample code handles a "press enter" action, which also sends the text to the server. You may want to automatically test it:
@Test
public void pressEnterMoreThan4chars() {
// Arrange
Browser.fillText(nameField(), "Enter");
Event keyUpEvent = EventBuilder.create(Event.ONKEYUP).setKeyCode(KeyCodes.KEY_ENTER).build();
// Act
Browser.dispatchEvent(nameField(), keyUpEvent);
// Assert
assertThat(dialogBox()).isShowing().textEquals("Remote Procedure Call");
assertThat(serverResponseLabel()).htmlEquals("Hello, Enter!<br><br>I am running mocked-server-info.<br><br>It looks like you are using:<br>mocked-user-agent");
assertThat(errorLabel()).isVisible().textEquals("");
}
-
In the layout phase, a GWT
Event
is created through the gwt-test-utilsEventBuilder
. It has been designed to build very complex events easily. -
Browser.dispatchEvent
is a generic method to simulate events your own custom events.
Note: Actually, the Browser
class provides a lot of helper methods, such as Browser.keyUp(Widget, KeyCode)
which would be more appropriate for this test. But we just wanted to introduce the EventBuilder
here :-)
Well, this was just an introduction about what gwt-test-utils is able to do. If you are interested in the many other features the framework provides, you should consider reading the complete documentation (which isn't to long ;-))
Of course, the GwtTestSample eclipse project can be downloaded under the Download section.