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


MockLib: Simulate APIs for Testing : Page 2

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.

MockLib Basics
Before diving into the examples, let's go over some MockLib basics. One of the most basic rules in MockLib is that you must call mockobject.expect(<methodName>) after calling the <methodName> on the MockObject. For example, the following code would fail:

MockObject mockObj = MockObjectFactory.createMock(Phone.class);
Phone mockPhone = (Phone)mockObj;
CalledMethod m = mockObj.expect("makeCall"); //we expect "makeCall" method to be called
//"makeCall" is called below but it is too late. Your test failed on the line above.
mockPhone.makeCall(new InetSocketAddress("", 5060));

On the other hand, if you called myMock.expect("makeCall") after mockPhone.makeCall() the test would pass since the program expected makeCall to be called and it was called just before. The expected method must occur after the actual method call because the expected method returns the CalledMethod object, which contains a few things including:

  • Values passed into the makeCall method (in this case, an InetSocketAddress)
  • The stack trace showing how the method was called (useful for debugging)

Since the parameters passed to the method are given back to the test, the test can pick them apart to verify every detail of the params passed into makeCall. In the example above, m.getAllParams[0] would return the InetSocketAddress. The test can then retrieve the IP and port and verify that they are and 5060. This is one of the main reasons MockLib's API is only six classes and so easy to learn.

Naturally, if a test wants a method to return a value or to throw an exception when it is called, it has to declare that before the method is called. Using the example above, if you want to throw an exception on your makeCall method, you would write the following code:

MockObject mockObj = MockObjectFactory.createMock(Phone.class);
Phone mockPhone = (Phone)mockObj;
mockObj.addThrowException(new RuntimeException("exc to test robustness"));
try {
mockPhone.makeCall(new InetSocketAddress("", 5060));
fail("should have thrown a RuntimeException");
} catch(RuntimeException e) { }
CalledMethod m = mockObj.expect("makeCall");

Notice you still have to call the expect to make sure makeCall is called. Because the expect is called, you can still retrieve the parameters passed into makeCall and make sure they are correct. These basics should help you follow the examples to come.

Author's Note: For the sake of simplicity, the tests in this article depend on the implementation in one fashion: they call new XXXServiceImpl(). This is a bad practice when testing APIs and I don't recommend it. The tests should depend only on the API and typically should go through a Factory. Then you can use Verifydesign to guarantee that the tests depend only on the API. Throughout this article, you will see red lines in the figures indicating where the APIs being used or simulated are. In a real world situation, these APIs would be much bigger. The last example demonstrates a bigger API and how to simulate any API.

Running the Code Examples
To run the code examples, you must:

  1. Use Ant 1.6.5 (1.7.0 has a known bug with JUnit).
  2. Copy mocklibExamples/tools/ant-junit/junit.jar to the $ANT_HOME/lib directory.
  3. Run the following in the mocklibExamples directory: ant –f bldfiles/build.xml.

The code snippets throughout this article contain line numbers matching the line numbers in the files included in the accompanying code download. (These examples are also posted on the web for reference.) Also, when I write code, I prefer to start with the test first if I can. The examples reflect this preference.

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