Tuesday, January 31, 2012

Introduction

JMock is the famous library which can be used for working with Mock Objects.
(Please check www.MockObjects.com to know more about Mock Objects.) . How ever you might be excited to know that Mock Objects are not just Stubs!! They give a different approach to unit testing.
So finally testing is also becoming as interesting as Coding.
In simple words we can say that a mock object is something which can be used for testing the behavior of other objects. It will mimic the behavior of an actual class / interface.
A Mock object can also observe how other objects uses its methods and compare with the expected behavior. (I will explain this in detail with code sample.)

This means that we can test and verify how our class uses the available environment. 
Let's assume you are developing a Customer class which has to interact with a BookShopclass. But unfortunately BookShop class your friend is developing and as usual it is not available yet. So naturally we will decide to use a Mock Object of BookShop instead of an actual instance. 
Customer has function doTransaction().
BookShop has methods pay(Money) , Book getBook()
Using JMock we will be able to specify that getBook() is always called only after pay().
So if anywhere in our doTransaction method if getBook is called with out pay() it will result in an error with bright red colour while testing it. 
This way we can ensure that our class uses the available interface in the desired way.
In a TestDrivenDevelopment environment this means that interface problems can be found even before the actual coding of it starts.
JMock makes our life easier by creating mock objects dynamically and providing features to specify expectations (How it should behave, etc).


Download Link

The JMock jar/zip can be downloaded from www.JMock.org
This can be added as external jar file in your Eclipse project.

Using JMock

Here comes the interesting part!
Lets develop as class called DispFile , which is a highly useful class for displaying a given file in console.
DispFile class uses the IFileHandler interface for the file operations .
Code for IFileHandler

public interface IFileHandler {   public void open(String filename) ;   public void close() ;   public String read() ;    } 

Code for DispFile 
public class DispFile {     public DispFile()  {     }    
     /**      * Business method to display the given file line by line      */     public boolean display(String filename)  {         
  Boolean status = true;         
  try   {             
   IFileHandler fileIntf = new FileHandler();             
   fileIntf.open(filename);             
   String line = null;             
   while ((line = fileIntf.read()) != null)    {                 
    System.out.println(line);             
   }            
   fileIntf.close();         } catch (Exception e)    {             
  Status = false;         
 }        
 return status;     } } 

 Here the line "new FileHandler" is the problem! We know the actual implementation is not yet available. So we have to use a MockObject for IFileHandler instead of an actual instance.  We must change the class to make it testable . The changes are minimal and it is a low price to pay for getting the testability with Mock Objects. This refactoring also makes the design cleaner.  The modified code is given below. 


public class DispFile {   
... /** Business method to display the given file line by line */   
public boolean display(String filename) {    
Boolean status = true; 
Try {     
IFileHandler fileIntf = getFileHandler();     
 ....       
}catch (Exception e) { 
Status = false 
}     
return status;  
 }   
/**   Its true that this method seems nonsense, but it is very important forUnit testing with Mock Object technology. */ 
protected IFileHandler getFileHandler() {  
return new FileHandler(); 
}    
} 

Code for Test Class 
package chkJMock;  
import junit.framework.TestCase;  
import org.jmock.*;  
public class DispFileTest extends MockObjectTestCase {   
/ *    * Test method for 'chkJMock. DispFile.display(fileName)'    */   
public void testDisplay() { ....   }  
} 

We will check the code             required in testDisplay method in detail. 


//set up the Mock Object .
//First we have to set up the context             in which our test will execute.
//We have to create a DispFile to test. We             have to create a mock IFileHandler that       
//should receive the message. We then             register the mock  IFileHandler with the DispFile.
Mock mockFileHandler = mock(IFileHandler.class); 
final IFileHandler intf = (IFileHandler) mockFileHandler.proxy();     
DispFile df = new DispFile() {      
protected IFileHandler getFileHandler() {        
// I hope now the use of silly looking getFileHandler method is clear.          
return intf;
// expectations     
// Here we specify how DispFile class is expected to use the IFileHandler interface.      
//     
// The method open should be called only once with the argument file (String            // containing name of the file to display).     
String file = "E:\\@Com XML CorbaTask.xml";     
mockFileInterface.expects(once()).method("open").with(eq(file));         
//     
// The method close should be called only once and it must be called after the method       
// open . (Unless you want to close a file which is not yet opened! ).     
mockFileInterface.expects(once()).method("close").after("open");      
// The method read should be called one or more times (we are reading lines in a loop.)     
// It should return a null string when it is invoked.     
mockFileInterface.expects(atLeastOnce()).method("read").withNoArguments()     .will(returnValue(null));         
// execute     
// Call the business method we were waiting for     
boolean status;     
status = lf.list(file);     
assertTrue("Error displaying file : " + file, status);  
This when executed ensures that the methods in the interface (Mock object ) are invoked in correct order and in specified way. If anything is violated (e.g- if close is called without calling open , open called multiple times ) , it will result in test error.  

Note :

To mock concrete class jmock-cglib extension and cglib jar should be added to the eclipse project. Testcase should derive from cglib.MockObjectTestCase.
Eg:-          
Mock mockTaskDetailsClient = mock(TaskDetailsClient.class,"mockTaskDetailsClient");  
final TaskDetailsClient taskDetailsClient = (TaskDetailsClient)mockTaskDetailsClient.proxy() ;   
ctr with args. mock(TaskDetailsClient.class,"mockTaskDetailsClient", class[],Object[]); 
There are many tools/ services available now which will help in generating unit test for the existing legacy code base. One such service is www.UnitTestFactory.com