lunes, 15 de octubre de 2012

Testing GWT Service Classes synchronously using syncproxy

Testing GWT Service Classes synchronously using syncproxy


Why ?


In GWT applications that use RPC for client-server comunication, RPC "Services" classes bring a Web API to our business logic core in the server. For example, an RPC service class may be responsible of managing a certain model class (its CRUD operations, etc). It is important for us to write automated tests for ensuring a certain degree of quality of those RPC services, automatically.

In a common - not GWT application we just write Junit serverside tests that will use our services classes asynchronically. For example, a service for a managing Apples, could be easily tested with the code:

Apple a1 = new Apple(); 
a1.setId("a1"); 
a1.setColor("red"); 

appleService.addApple(a1); 
Apple a2 = appleService.getAppleByName("a1"); 
assertEquals("red", a2.getColor()); 

appleService.removeApple(a1); 
a2 = appleService.getAppleByName("a1"); 
assertTrue(a2 == null); 

Unfortunately, GWT RPC service class methods, are called asyncrhonously, so it is very hard to write the same test case, because, we need to"continue the next test " inside the (async) callback. So for writing  the last example using RPC async calls, we will end up with code that contains 4 or 5 anidated code blocks, like:

appleService.addApple(a1, new AsyncCallback<Void>() {

 @Override
 public void onSuccess(Void result) {

  appleService.getAppleByName("", new AsyncCallback<Apple>() {

   @Override
   public void onSuccess(Apple result) {
    assertTrue(result.getColor().equals("red"));

    // :( ... test folows here ... :(
   }

   @Override
   public void onFailure(Throwable caught) {
   }
  });
 }

 @Override
 public void onFailure(Throwable caught) {
 }
});

On a first thought I tried to remedy this using a datastructure I called SyncQueue, for putting each service method call in a queue and at the end, ask the sync queue to run each task synch. But as this example shows, this is also a not so productive and readable way of writing tests.

How to ? 


Thinking this for some time get me to the conclusion that I really need to write JUnit test calling my RPC service methods synchronously (like the first code example). The project syncproxy seemed what I wanted but it wasn't so trivial to getting started with, mainly because documentation seems a little outdated.

So these are my instructions for making synch tests of your RPC services, using eclipse and google eclipse  plugin.

A summary of the entire procedure:
  1. create a new GWT Application project using eclipse New Application project wizard code template that comes with an example RPC service to test against to. This will be our "target" GWT application which we want to test.
  2. create a (second) eclipse Java Project, add syncproxy.jar and previous "target" GWT project to its build path. 
  3. create a JUnit  TestCase class in the test project that calls the target service class synchronously. The test app is a java client that tests against the "target" running GWT application.

First of all, I suppose you already have a GWT application which GWT services you wish to write tests for. If you don't just create a new GWT Web Applicaiton project with asking the wizard to create the template project that contains an already running RPC service for testing. That is what we will use in this example, so

Step 1 (optional): create a GWT project with a RPC service to test. 

Go to File -> New -> Other... -> Google -> Web Application Proje. Unselect Support for appengine and make sure "Generate sample code" is checked: 




Press Finnish and test the new application if you want. In my case I named it "Gwtrpcapp1" so I right click the file /war/Gwtrpcapp1.html -> Debug as .. -> Web Application . After is loaded, open the url http://127.0.0.1:8888/Gwtrpcapp1.html?gwt.codesvr=127.0.0.1:9997 in your browser.

This project cointains a RPC service called "greet". The data that we need from this RPC service are its module base url ("http://127.0.0.1:8888/gwtrpcapp1/") and the name of the service that we wish to test ("greet"). I found these values using Firefox's Firebug extension for examining HTTP request of our app:


Step 2: Create your test project


We will create a second project containing our junit tests. We choose a second project but if you are familiar with the tools you should not have major problems for putting these tests inside the same GWT project. In this case go to File -> New -> Java -> Java Project and create one. In my case I named it "Gwtrpcapp1Test1".

Now let's configure it a little this new project, first of all add the our target GWT project to its java class path. Right click project "Gwtrpcapp1Test1" -> Build Path -> Configure build Path -> Projects -> Add.. and choose our  target in my case "Gwtrpcapp1".

Also download syncproxy.jar from http://code.google.com/p/gwt-syncproxy/downloads/list,. copy syncproxy.jar file inside project "Gwtrpcapp1Test1", and add it to its class path.

Also you will need to add gwt-servlet.jar to your test project build path. Just go to Configure build path -> Libraries -> Add jars and choose the file  gwtrpcapp1/war/WEB-INF/lib/gwt-servlet.jar.


Now create some java package to work and create a Junit Test right clicking the package -> New .. -> Other -> Java -> JUnit -> JUnit Test Case. Eclipse will ask permission for adding JUnit library in your project. Complete some simple test there:

package org.sgx.gwtrpcapp1.test;

import static org.junit.Assert.*;

import org.junit.Test;
import org.sgx.gwtrpcapp1.client.GreetingService;

import com.gdevelop.gwt.syncrpc.SyncProxy;
/**
 * test for GreetingService - url : http://127.0.0.1:8888/gwtrpcapp1/greet 
 * @author sg
 *
 */
public class GreetingServiceTest1 {
 private static final String MODULE_BASE_URL = "http://127.0.0.1:8888/gwtrpcapp1/";
 private static final String SERVICE_NAME = "greet"; 
  
 private static GreetingService greetingService = (GreetingService) SyncProxy
  .newProxyInstance(GreetingService.class, MODULE_BASE_URL, SERVICE_NAME);

 @Test
 public void test() {
  System.out.println(greetingService.greetServer("Sebastian"));
  assertTrue(greetingService.greetServer("Sebastian").startsWith("Hello, Sebastian!"));    
 }
}

Important:  For this to work, all the classes that you want to pass / return from RPC methods must implement the com.google.gwt.user.client.rpc.IsSerializable interface.

 Run the test


If everything was fine, first run the target GWT Application like I told you before, and after its loads complete, run the our JUnit test. For that just right click GreetingServiceTest1 file -> Run as -> JUnit Test Voila! Hope this can be helpful to others in a similar situation.

Google AppEngine DataStore

If you are testing RPC services that perform some CRUD operations in Google Appengine DataStore these few tips could be helpful. Also this apply for frameworks that run on top of appengine datastore, like objectify.

The datastore it self is not synchronically: it takes some seconds to really impact changes to the datastore. So for example, if in our synchronous JUnit tests we save an entity it could not be available yet in the next data query. The same for deletion, it takes aprox 5 seconds to the datastore to really impact the entity deletion.

This is bad for our synchronous JUnit tests where we want the changes to impact synchronously. Fortunately, it is easy to configure the appengine in devmode for the datastore to behave synchronously. Just go to your appengine project launch configuration -> Appengine and edit the "unapplied job percentage" value to a very small number like 0.001:

Now your datastore will behave synchronously (at least almost all the time).

Also, I successfully run this test against my application deployed on appspot.com. I thought the test will fail because the weak consistence nature of the datastore, but surprisely, the test finnish without errors. Coud this be because of my slow intenret connection. Or, more worry, could this be because my eclipse application launcher configuration is somehow deplyed also ? ... mmm will have to investigate...

1 comentario:

Rock Den dijo...

Working with Your First Testing Services, demonstrates the basic constructs of a soapUI project—TestSuites, TestCases, and TestSteps—which prepares you for the next chapters of the book. We will also look into the validation of responses using
assertions and soapUI properties.