Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

MockLib: Simulate APIs for Testing : Page 5

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.


advertisement
Simulating Entire APIs
One of my favorite aspects of MockLib is its ability to simulate an entire API. One of the major problems in simulating an entire API is that it can return a huge object that you need to simulate yet again. It is very important when simulating an entire API to be able to return mock objects from mock objects. The next example will demonstrate this.

This example is a system that receives your emails and when a high-priority email comes in, it calls your cell phone to make sure you check your email ASAP. The EmailToPhone system uses a phone API, simply a PhoneFactory that creates phones (see Figure 4). Not too glamorous but it gets the point across.

Click to enlarge

Figure 4. Unit Testing with Timer Simulation



As usual, you start by creating the mock objects and the EmailToPhone system. You also make sure you pass the MockFactory into the system so the EmailToPhone system can create phones when it needs them:

38 mockPhoneFactory = MockObjectFactory.createMock(PhoneFactory.class); 39 mockPhone = MockObjectFactory.createMock(Phone.class); 40 41 sysUnderTest = new EmailToPhoneImpl((PhoneFactory)mockPhoneFactory);

Next, before your test actually calls the receivedHighPriorityEmail method, you must add a return value since createPhone will be called and needs to return a phone. At this point, when writing the test you have decided the implementation may work a certain way. Of course, after you write the implementation you might want to change the test slightly. For now though, assume every time receivedHighPriorityEmail is called, you will create a phone and make a call:

57 //We know that when receivedHighPriorityEmail is called, the EmailToPhone system will 58 //create a Phone so you need to make sure the mock object's passes back a mockPhone 59 //that the test controls 60 mockPhoneFactory.addReturnValue("createPhone", mockPhone);

Next, you have to simulate receiving a high-priority email:

62 String extension = "555-4444"; 63 String emailTitle = "xxxx"; 64 String emailContents = "yyyy"; 65 sysUnderTest.receivedHighPriorityEmail(extension, emailTitle, emailContents);

Lastly, you expect that createPhone was called on the mock PhoneFactory. You then expect the makeCall method to be called on the mock phone that you returned from the mock PhoneFactory. On line 72, you also expect to be calling the right phone number (i.e., verifying makeCall was given the correct extension):

67 //we expect createPhone to be called 68 mockPhoneFactory.expect("createPhone"); 69 70 //we expect a phone call to be made to the correct extension 71 CalledMethod m = mockPhone.expect("makeCall"); 72 assertEquals(extension, m.getAllParams()[0]);

Again, try to implement the EmailToPhone system yourself. One solution is located solution here.

More Capabilities
As you use MockLib more and more, you begin to run into more advanced scenarios. Sometimes, you want to throw an exception the first time a method is called and return a value the second time or vice-versa. MockLib maintains a queue for each method on a class. The methods that add to this queue are:

  • addReturnValue – queues a return value to be returned
  • addThrowsException – queues an exception to be thrown
  • addBehavior – queues a snippet of code to be run when the method is called

All of these methods add to that same queue. So the first time a method is called, a value could be returned; the next time, an exception could be thrown; and finally, a code snippet could be run. They all use the same queue. If this queue happens to be empty, a default value must have been specified. The default can be set with either of these methods:

  • setDefaultBehavior – sets a default snippet of code to be run when the method is called
  • setDefaultReturnValue – sets a default return value to be returned

Sometimes, you encounter methods like getId() that you really don't want and would like to ignore. After all, every time you add a new log statement using getId() you would have to change your test case, which is a maintenance nightmare. Instead, tell the MockObject to ignore that method by calling addIgnore("getId");.

MockObject can also deal with multi-threaded systems. By default, it will wait a few seconds for the method it expects to be called (if it has not already been called). There is also a setExpectTimeout to override this default and allow longer times. It can be useful for debugging as well.

Last but not least, MockLib offers a special clone feature. The Behavior class forces you to implement a method that clones the parameters. This allows the test to take a snapshot of what the values of the parameters were when they were passed in. After all, they could change later in the implementation code and your test would fail.

A Power-Packed Six Classes
MockLib offers much more than what this article showed. Even though the API is only six classes, it is still very powerful. Here are some things it enables you to do that weren't covered:

  • Tell the MockObject to throw an exception
  • Tell the MockObject to return a value
  • Run snippets of code when a method is called
  • Clone parameters passed into a MockObject
  • Retrieve info on how a method was called

You did however learn the differences between testing a listener implementation and mocking a listener. You also saw how to test timer-triggered events in mere nanoseconds. Lastly, you witnessed MockLib's power to simulate an entire API. For more examples on complicated tests, visit these open source projects:



Dean Hiller creator of Verifydesign and Mocklib, is the Director of Architecture, where he uses tools like verifydesign to monitor evolving architectures, teaches teams how to be disciplined enough to follow true Agile processes. His teams produce products where the minimum amount of code tested by automated tests is 75 percent.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap