Distributed JUnit testing with GridGain

Recently we have been running some problems while testing distributed applications with JUnit. The main problem was that we were running the client and the server within the same host. Although the test passed because the application logic was correct. Running the application distributed failed due to some errors related with the naming of the processes that were related to localhost.

How to test distributed applications with JUnit?. The only product that can do that is GridGain. GridGain is an open source framework for distributed computing. It also provides a way of running tests distributed across many nodes. This means that we can parallelize our JUnit tests on different nodes and improve the overall time to get them done. But what it is more important for us. How to test distributed applications that require client server interaction.

Configuration
GridGain is easy to set up. Once you have downloaded and uncompressed the zip file. You only have to set up two environment variables JAVA_HOME and GRIDGAIN_HOME. Now you are ready to launch your remote execution node by executing the following script ./gridgain-junit.sh

Let’s make a JUnit test

I am using eclipse, but you can use your preferred java builder. Once you have created a project you only have to import the jar files included in the libs folder and also the gridgain-2.0.3.jar file. And you will be ready to implement you unit tests.

  • ServerTest class: This test just runs a server socket waiting for incoming calls. Reads a Date object instance sent by the client and prints it out.
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Date;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ServerTest {
    /**
     * Set up logic.
     * @throws Exception Thrown in case of any error.
     */
    @Before
    public void beforeTest() throws Exception {
        System.out.println("Preparing for test execution: " + getClass().getSimpleName());
    }

    /**
     * Tear down logic.
     * @throws Exception Thrown in case of any error.
     */
    @After
    public void afterTest() throws Exception {
        System.out.println("Tearing down test execution: " + getClass().getSimpleName());
    }

    /**
     * Example test method.
     */
    @Test
    public void testServer() {
      boolean stop = false;
      ServerSocket serverSocket = null;
      final String hostName = "gaiawl06.net4.lan";
      try {
          InetAddress addr = InetAddress.getLocalHost();

          // Get IP Address
          byte[] ipAddr = addr.getAddress();
          // Get hostname
          if (hostName.compareTo(addr.getHostName())==0){
              serverSocket = new ServerSocket(4000);
              while (!stop){
                  Socket client =  serverSocket.accept();
                  System.out.println(getClass().getSimpleName() +" Incomming DATA! " + client.getInetAddress());
                  ObjectInputStream in = new ObjectInputStream(client.getInputStream());
                  ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
                  final Date date  = (Date)in.readObject();
                  System.out.println(getClass().getSimpleName() +" Socket read Object from client " + date);
                  in.close();
                  out.close();
                  stop = true;
              }
          }
      } catch (UnknownHostException e) {

          e.printStackTrace();
      } catch (IOException e) {
          e.printStackTrace();
      } catch (ClassNotFoundException e) {
          e.printStackTrace();
      }finally{
          try {
              if(serverSocket != null){
                  serverSocket.close();
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
      System.out.println(getClass().getSimpleName() + "Output from TestC.testMethod1().");
    }
}
  • ClientTest class: This test just opens a socket to the remote server and sends a Date object instance to the remote server.
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Date;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ClientTest {
    /**
     * Set up logic.
     * @throws Exception Thrown in case of any error.
     */
    @Before
    public void beforeTest() throws Exception {
        System.out.println("Preparing for test execution: " + getClass().getSimpleName());
    }

    /**
     * Tear down logic.
     * @throws Exception Thrown in case of any error.
     */
    @After
    public void afterTest() throws Exception {
        System.out.println("Tearing down test execution: " + getClass().getSimpleName());
    }

    @Test
    public void testClient() {
        try {
             System.out.println(getClass().getSimpleName() +" Wait 5 seconds server to start ");
             Thread.sleep(5000);
             final String hostName = "xxxxxxxxxxx";
             // open a socket connection
             Socket socket = new Socket(hostName, 4000);
             // open I/O streams for objects
             ObjectOutputStream ou = new ObjectOutputStream(socket.getOutputStream());
             ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
             ou.writeObject(new Date());
             ou.flush();
             ou.close();
             in.close();
           } catch(Exception e) {
             System.out.println(e.getMessage());
          }
    }
}

You have to bear in mind that the ServerTest has to run at the remote host were the GridGain daemon is running. Just if you want the client to be able to connect the remote server.

Now is were the magic comes. We have to ensure that the ServerTest runs on the remote host and the ClientTest runs on the local host (my computer). In order to get this working we need three more classes:

  1. RemoteTestSuite
  2. LocalTestSuite
  3. DistributedTestSuite
  • RemoteTestSuite class: It is used to set up the tests we want to run distributed. We only have to add the @GridifyTest annotation and we will enable this suite to run in our GridGain grid. This does not prevent the test to run locally, so if we want to enforce the test to run on the remote host we have to add the following JVM parameter -DGRID_ROUTER_PREFER_REMOTE=true
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.gridgain.grid.test.GridifyTest;

@RunWith(Suite.class)
@SuiteClasses(ServerTest.class)
@GridifyTest
public class RemoteTestSuite {
}
  • LocalTestSuite class: is used to run the client test locally. We only have to add the @RunWith(GridJunit4LocalSuite.class) annotation to enforce this test to run locally.
import org.gridgain.grid.test.junit4.GridJunit4LocalSuite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(GridJunit4LocalSuite.class) // Specify local suite to run tests.
@SuiteClasses(ClientTest.class)
public class LocalTestSuite {
}
  • DistributedTestSuite class: is used to configure the distributed test we want to run. In our case is composed of two tests that will run in parallel and distributed. You will have to add the following annotation @RunWith(GridJunit4Suite.class) if you want GridGain to run the test.

This class is the one you have to run in your eclipse project and before running it you have to set up the JVM with the parameter I mentioned before.

import org.gridgain.grid.test.junit4.GridJunit4Suite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(GridJunit4Suite.class)
@SuiteClasses({
    RemoteTestSuite.class,
    LocalTestSuite.class, // Local suite that will execute its test locally.
})
public class DistributedTestSuite {
}

Conclusions
GridGain is based on JGroups so be sure that the agents are on the same network If they do not see one each other you will not  be able to send your tests to the remote node. It is pretty easy to set up and make it run. Even getting your Junit tests being executed distributed, it is very easy.
This product fills the gap for testing distributed applications with JUnit.

1 Comment on “Distributed JUnit testing with GridGain”

  1. #1 bob
    on Dec 16th, 2010 at 10:56 am

    hostName.compareTo(addr.getHostName())==0
    can be written as
    hostName.equals(addr.getHostName())

Leave a Comment