Mon
24
Jul '06
|
|
Sorry for another non-ruby post, but I’m currently very busy at work playing with JBoss 4, EJB 3, EasyMock, and a bunch of other pretty cool technologies.
I’m a big fan of EasyMock but frequently I ran into problems when code performed lookups to get resources like:
Context ctx = new InitialContext();
UserTransaction trans = (UserTransaction) ctx.lookup("UserTransaction");
trans.begin();
At first it looked like I had to change the code to somehow inject my mock objects. But fortunately there is an easier, more elegant solution.
Java uses an instance of InitialContextFactory to create the InitialContext object. So the solution was to write a mock object for InitialContextFactory and tell Java to use it instead. This mock object must be a ‘real’ class (not one created with EasyMock) that can be found by reflection.
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
/**
* Provides a JNDI initial context factory for the MockContext.
*/
public class MockInitialContextFactory
implements InitialContextFactory {
private static Context mockCtx = null;
public static void setMockContext(Context ctx) {
mockCtx = ctx;
}
public Context getInitialContext(java.util.Hashtable< ?, ?> environment)
throws NamingException {
if (mockCtx == null) {
throw new IllegalStateException("mock context was not set.");
}
return mockCtx;
}
}
To use it for my piece of example code, I have tell Java which InitialContextFactory to use (via java.naming.factory.initial), create a mock Context, create a mock Transaction, tell the mock Context to return the Transaction. And finally tell the mock factory to return the mock Context. This sound much more complex than it actually is, here’s the same in code:
System.setProperty("java.naming.factory.initial", "MockInitialContextFactory");
Context mockCtx = createMock(Context.class);
UserTransaction mockTrans = createMock(UserTransaction.class);
expect(mockCtx.lookup("UserTransaction")).andReturn(mockTrans);
replay(mockCtx);
MockInitialContextFactory.setMockContext(mockCtx);
Maybe the most elegant solution would be to inject a InitialContextFactory via Setter-Injection. But if you don’t use IOC, the Factory-Pattern is a good choice i think
The code looks exactly like the example and I didn’t want to change the code just for testing.