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.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

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); 11 12 public abstract void deleteUser(String userName); 13 14 public abstract void addUserListener(UserListener l); 15 16 public abstract void removeUserListener(UserListener l); 17 } 05 public interface UserListener extends EventListener { 06 07 public void userAdded(String userName); 08 09 public void userDeleted(String userName); 10 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); 069 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); 049 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); 078 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.

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