Introduction


The intent of this blog entry is to track what I learn while working through an example using JUnit 4.3.1 and jMock 2.5.1. This entry is a work in progress and will be updated as I go along. By the end of this, I think you will agree that jMock is awesome and can provide you with a very good tool to mock-up something that might be heavyweight and unit testing in general is absolutely critical to production software.

JUnit


Let's get the introductions out of the way for those who don't know about JUnit. Put simply, JUnit is an open source testing framework used to test source code. It is critical for any production application to include a thorough set of unit tests so that errors can be detected immediately, even before checking new code in. Every class should have a unit test to verify that it behaves as the author expected and to ensure this is the case, a unit test should be written along with the new class so that tests can be set up immediately. Any subsequent changes to the class can be verified by re-running its test before the changes are commited to the repository. If an untested scenario is discovered, then it can be added to the unit test so that the unit test evolves with the class so that future changes that cause the same problem can be detected immediately and fixed.

jMock


jMock is a library that supports test-driven development of Java code with mock objects. Mock objects are useful because they can help you test the interaction between the objects in your program. Here are some bullet points from their website:

The jMock library:

  • makes it quick and easy to define mock objects, so you don't break the rhythm of programming.
  • lets you precisely specify the interactions between your objects, reducing the brittleness of your tests.
  • works well with autocompletion and refactoring of your IDE
  • plugs into your favorite test framework
  • is easy to extend

Now, imagine you need an object that requires an LDAP (lightweight directory access protocol) server to construct. Now, you could start an LDAP server, give it some data, run your test and then tear it down, but that's a lot of work and more closely resembles integration testing. However, a faster alternative would be to use jMock to mock up an object that returns the data you need. Mocking up objects is only the tip of the iceberg. You can also set expectations of objects (e.g. this method is only called once), set the order methods should be called in, test multi-threaded code, etc.

Example


In this example, we will create a mock object of a Sensor with jMock and add it to a SensorNetwork. We will set the expectation that the sensor receive a message from the sensor network. If no message is received, then the test will fail. This example is intended to be simple in order to illustrate the potential of jMock. It is worthwhile to note that we will extend MockObjectTestCase which uses the legacy JUnit 3 classes. The reason for this is that eclipse comes bundled with JUnit 4.3.1 and to use the @RunWith(JMock.class) notation of the latest version of jMock requires JUnit 4.4 or later. To add JUnit 4.4 or later, we would have to delete the JUNIT4 folder from the Eclipse plugins directory to avoid a security exception thrown because Eclipse will load it's JUnit 4 before ours. We want to avoid altering the Eclipse plugins directory, so we will use as much JUnit 4 notation as possible until the next Eclipse update. This example only would require a few minor modifications to run with the latest jMock using JUnit 4.4+ so if you want, you can make the altercation and see the jMock website for the minor changes to our test case.

Setup

  1. Get the plugin org.jmock (2.5.1) from MAEviz SVN (svn+ssh://subversion.ncsa.uiuc.edu/CVS/ncsa-plugins).
  2. JUnit 4.3.1 or later.

That's it for external plugins. Now, we'll need to create a project to contain our class under test and a project to contain the tests. I prefer the following packaging conventions:
ncsa.myexample - eclipse plugin
ncsa.myexample.tests - eclipse fragment with ncsa.myexample as the host plugin

After creating those projects, we'll need to create something to mock up and test. First, we are going to create a simple Sensor interface that can receive a message and a SensorNetwork class that contains some sensors and can publish messages to them.

Sensor.java
// interface class for a sensor

// send the sensor a message
public void receive( String message );
SensorNetwork.java
// contains a network of sensors, simplified with one real method to publish messages

private List<Sensor> sensors;

public void publish( String message)
{
  for(Sensor sensor : getSensors()) {
    sensor.receive( message );
  }
}

public void addSensor(Sensor sensor)
{
  getSensors().add( sensor );
}

public List<Sensor> getSensors()
{
  if(sensors == null) {
    sensors = new LinkedList<Sensor>();
  }
  return sensors;
}

Next, we need to create a test class to test out our sensor. You should create the following class in your test fragment:

SensorNetworkTest.java
public class SensorNetworkTest extends MockObjectTestCase {


  SensorNetwork network;

  @Before
  public void setUp() throws Exception
  {
    network = new SensorNetwork();
  }

  @Test
  public void testOneSensorReceivesMessage() throws Exception
  {
    // Our mock object
    final Sensor sensor = mock( Sensor.class );

    // Add our mock sensor to the network
    network.addSensor( sensor );

    final String message = "Hello World";

    // Define Expectations
    checking( new Expectations() {
      {
        oneOf( sensor ).receive( message );
      }
    } );

    // send a message, without this line the test will fail
    network.publish( message );
  }
}

The last step will be to run this as a unit test, not a JUnit Plug-in Test. If you try to run it as a JUnit Plug-in Test, it will fail saying it cannot find certain classes because it needs JUnit 3.8.2 or 3.9.0 so there are definitely some odd dependency issues where some parts of eclipse seem to depend on JUnit and you get a clash between JUnit 3 and JUnit 4. I have only been able to successfully use JUnit 3 as a Plugin Test; however, you would need to remove the JUnit 4 annotations and run everything as JUnit 3.

Problems Encountered


  1. I get a java.lang.SecurityException when trying to run my unit test.
    • This exception occurs because in the Eclipse plugins folder there is a JUNIT4 folder which contains junit.jar version 4.3.1 and lower in the build we are using org.junit version 4.4, which contains JUnit4ClassRunner, a class that we need. The solution is to delete the folder in the plugins directory so that the latest junit 4 is used. There is no way around this because we need that class runner to use JMock.
  2. My tests pass even though I have set an expectation using JMock that does not pass (e.g. method must be called once and it is not called).
    • This can happen if at the top of the class you have not specified that the test should be run with JMock by using the tag @RunWith(JMock.class)

Links


Links to the libraries used in the above example.

  • No labels