miércoles, 26 de marzo de 2014

Mocking JNDI

See:


Testing components that use JNDI to get Queues and other resources can be annoying, because (hopefully) you don't want to run your unit tests inside the application server. 
When you can't turn around the JNDI to get your resources and inject them into your objects... it may be the case of mocking the JNDI system with something that you can control from your tests. SO mockS a minimum set of the JNDI system with an in-memory implementation. This is what I did.


public class InitialContextFactoryForTest implements InitialContextFactory {

private static Context context;

static {

try {
context = new InitialContext(true) {
Map<String, Object> bindings = new HashMap<String, Object>();

@Override
public void bind(String name, Object obj) throws NamingException {
bindings.put(name, obj);
}

@Override
public Object lookup(String name) throws NamingException {
return bindings.get(name);
}
};

} catch (NamingException e) { // can't happen.
throw new RuntimeException(e);
}
}

public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
return context;
}

public static void bind(String name, Object obj) {
try {
context.bind(name, obj);
} catch (NamingException e) { // can't happen.
throw new RuntimeException(e);
}
}
}

public class SmsNotificationHandlerTest { 

@Before
public void init(){
   Queue mtQueueAsMock = getMtQueueAsMock();
     QueueSenderHome senderQueueAsMock = getQueueSenderAsMock();
     
     // sets up the InitialContextFactoryForTest as default factory.
     System.setProperty(Context.INITIAL_CONTEXT_FACTORY,            
     InitialContextFactoryForTest.class.getName());
     InitialContextFactoryForTest.bind("queue/gsg3_MT", mtQueueAsMock);
     InitialContextFactoryForTest.bind("QueueSender_3", senderQueueAsMock);
}

@Test
public void testSendSmsUsingServiceNameVariable() throws IOException {
  ...
  ...
  ...
 }
}

The InitialContextFactoryForTest.bind() method is used in the setUp() of the TestCase to bind object that will be needed by the tests, and then your code can use JNDI lookup as usual to get the resources they need.
InitialContextFactoryForTest is a very limited implementation, but it's usually enough to support most common usages of JNDI. Sometime it may be the case to extend it to support more functionalities.