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


MockLib: Simulate APIs for Testing : Page 3

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.

Mocking Listeners
Mocking a listener can be very useful for testing whether events are fired at the proper time. Also, it is good for verifying that the proper event object is used with the proper contents. To demonstrate this, the example to follow writes a test and implementation that looks like Figure 1.

Click to enlarge

Figure 1. Unit Test with a Mock Listener

You will test an API. It just so happens that there is only one implementation class behind this particular API. This is atypical of course, and used here just as a simplified example.

The first example is a UserService system from which the client can add/delete users. Clients also can register a listener with the UserService so they get notified when users are added and deleted. Here is the API of the user service you will test:

08 public interface UserService
09 {
10     public abstract void addUser(String userName);
12     public abstract void deleteUser(String userName);
14     public abstract void addUserListener(UserListener l);
16     public abstract void removeUserListener(UserListener l);
17 }

05 public interface UserListener extends EventListener {
07   public void userAdded(String userName);
09   public void userDeleted(String userName);
11 }

When the first client calls addUser after the second client already has added a listener, the userAdded method on UserListener will be called to notify the second client of the user being added. To write a test that guarantees this happens, you first need to create a mock listener. You do that like you would with most other mock libraries:

047     mockListener = MockObjectFactory.createMock(UserListener.class);

Next, you create the system you want to test and add the mock listener to it:

049     sysUnderTest = new UserServiceImpl();
065     sysUnderTest.addUserListener((UserListener)mockListener);

Now, the system you are testing has a UserListener it will fire events to when users are added/deleted. The next step is to add a user and verify an event was fired:

067     String user = "abc";
068     sysUnderTest.addUser(user);
070     CalledMethod method = mockListener.expect("userAdded");

Here, the test calls mockListener.expect, which expects only one method to be called on the UserListener. That is, it expects one—and only one—event to be fired to the listener. If two events were fired or userDeleted were fired, the test would fail.

Now you are still missing some verification here: you have not verified that the event was telling you the user "abc" was being added. There is still the potential for a bug. The following code is how you get the parameters passed into userAdded out of the cache:

071     String actualUser = (String)method.getAllParams()[0];
072     assertEquals(user, actualUser);

As you can see, you use this technique when you have an API that listeners can be added to, and when you are testing the implementation of that API.

The last thing to do is actually write the implementation. I will leave that up to you. If you have any trouble, here is a possible answer.

Testing Listeners
Next, let's move on to an example that tests the client sitting on top of the UserService. That is, you won't test the UserService anymore but instead test the client you just simulated in the previous example. This client is a replication server that listens for users that are added/deleted from the UserService. When it is notified that users have been added/deleted, it updates its cache to be the same as that of the UserService. Figure 2 shows what your test setup will look like.

Click to enlarge

Figure 2. Unit Test to Test the Listener Implementation

In the previous example, the test simulated UserReplicationService and created a mock UserListener. This example simulates a client of UserReplicationService, and UserReplicationService adds its real UserListener implementation to Mock UserService. You will test UserReplicationService's UserListener by firing events to that listener (i.e., simulate what UserService really does).

First, just like in the previous example, create your mock object and the system that you want to test:

048     mockUserSvc = MockObjectFactory.createMock(UserService.class);
050     //Create the System Under Test using dependency injection....
051     sysUnderTest = new UserReplicationServiceImpl((UserService)mockUserSvc);

This setup does not mock a listener but instead, adds a listener to your mock legacy system. The constructor of UserReplicationService calls addUserListener. Since all the MockLib MockObjects are just caches, the addUserListener will be called and you will retrieve the actual listener that was cached in the mock UserService:

053     //As soon as you create the System Under Test, you expect the 
054     //SysUnderTest will add a DisplayListener to the legacy system.
055     //You then retrieve that implementation of DisplayListener here
056     //for later use.... 
057     Object[] params = mockUserSvc.expect("addUserListener").getAllParams();
058     userListener = (UserListener)params[0];

This is where the true power of MockLib really shows. The test now has the listener, and can pretend to be the UserService and fire events into the system you are testing. Knowing that, pretend another client added a user. Fire some events in and make sure things operate as normal:

075     String user="fakeUser1";
076     assertFalse(sysUnderTest.doesUserExist(user));
077     userListener.userAdded(user);
079     //verify it was updated
080     assertTrue(sysUnderTest.doesUserExist(user));

That wraps up the test of your ReplicationUserService. Again, I leave the implementation up to you. If you have trouble, find the answer here.

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