RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


MockLib: Simulate APIs for Testing : Page 4

Using MockLib, a tool that simulates entire APIs, you can unit test contracts or well-defined APIs and refactor whole components with very few changes to your tests.

Passing the Timers
Now you'll see how to create unit tests when timers are involved. The example system is a CalendarService where events will be scheduled. When they go off, they will fire to listeners that are listening for the start of events. This example builds on the first. Notice Figure 3 is very much like the first example except for the addition of a MockTimer. It also shows more details of the system you are testing in that it contains a TimerTask with code that you will test.

Click to enlarge

Figure 3. Unit Testing with Timer Simulation

Before you start working on the timer system and tests, you need an interface for the timer used in Java. Start by creating TimerInterface.java with the following methods:

17 public interface TimerInterface {
19   public void cancelTask(TimerTask task);

24   public void schedule(TimerTask task, long delay);

29 }

An upcoming section will explain the purpose of adding the cancelTask method, which is not a method on Java's timer. For now, move on to the system you plan on testing. The API of that system is as follows:

05 public interface CalendarService {
07   public abstract void addEvent(String title, long delay);
09   public abstract void cancelEvent(String title);
11   public abstract void addScheduleListener(ScheduleListener l);
13   public abstract void removeScheduleListener(ScheduleListener l);
14 }

This system is for scheduling events in the future. When an event goes off, it will then notify the ScheduleListener listener. Now, the last part of the API is the ScheduleListener itself used to notify a client that the event has started:

16 public interface ScheduleListener extends EventListener {
18    /**
19    * @param title
20    */
21   void eventStarted(String title);
23 }

Looking at this API, you basically would like to add an event that will go off in 24 hours, simulating a real client. The only problem is you don't want to wait 24 hours for the test to run. To resolve this, start your test by creating your mock timer and your service just like in the previous examples:

044     //Create a mockTimer which acts as a cache for TimerTasks.....
045     mockTimer = MockObjectFactory.createMock(TimerInterface.class);
046     calendar = new CalendarServiceImpl((TimerInterface) mockTimer);

Now, just like the first example, your test needs to simulate adding a ScheduleListener. This is of course a mock ScheduleListener. Here is the initial code:

048     //Create and add a mockListener which you use to verify receiving of the 
049     //proper events.... 
050     mockListener = MockObjectFactory.createMock(ScheduleListener.class);
051     calendar.addScheduleListener((ScheduleListener)mockListener);

Now that everything is set up properly, you are ready to actually test the system. Start by adding an event to your CalendarService:

069     String title = "some event";
070     long delay = 50000;
071     calendar.addEvent(title, delay);

Which method do you expect to be called on your mock timer now? I will give you a hint: There are only two methods and one is only for canceling tasks. That's right, a TimerTask will be scheduled on the Mock Timer:

073     //When an event is added, you expect schedule on the timer to be called... 
074     CalledMethod method = mockTimer.expect("schedule");
075     //param[0] is the mock object's cached timer task....
076     TimerTask task = (TimerTask)method.getParameter(0);
077     assertEquals("Should have set the timer for "+delay+" ms", new Long(delay), method.getParameter(1));

Notice that right after you expect schedule to be called you grab the parameters that were passed to that schedule method—the TimerTask and the delay. You verify that CalendarService scheduled the event to happen 50,000 milliseconds in the future, so you know that is correct. However, there is no reason to wait 50,000 milliseconds to run the TimerTask; now that you retrieved the TimerTask, you might as well run it right away. When you run the TimerTask, you then immediately expect the Mock ScheduleListener to receive an event as well. The following code demonstrates this:

079     //We already verified it was scheduled in the future, so there is
080     //no need to wait till run the task then
081     //run the task now instead of waiting 50000ms
082     task.run();
084     //expect that the listener's eventStarted method is called with the
085     //property event title
086     method = mockListener.expect("eventStarted");
087     assertEquals("title should be the same", title, method.getParameter(0));

As usual, I leave it up to you to write the implementation of CalendarService. The first test is provided above. Also, as usual, you can find one solution here.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date