How to mock InitialContext constructor in unit testing - java

when I try to mock following method(Method is using remote EJB call for business logic) for the Junit test, it gives javax.naming.NoInitialContextException
private void someMethod(int id1, int id2, HashMap map){
......some code........
Context ctx = new InitialContext();
Object ref = ctx.lookup("com.java.ejbs.MyEJB");
EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
ejbBean.someMethod(id1,name);
.......some code.......}
My unit test for above method
#Test
public void testsomeMethod() throws Exception {
.......setting initial code...
//Mock context and JNDI
InitialContext cntxMock = PowerMock.createMock(InitialContext.class);
PowerMock.expectNew(InitialContext.class).andReturn(cntxMock);
expect(cntxMock.lookup("com.java.ejbs.MyEJB")).andReturn(refMock);
..........some code..........
PowerMock.replayAll();
Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map);
}
when the Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map) method invokes it gives following exception.
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
I believe, although we mock the Context in test method, it does not use the mock object when calling Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map) method, instead of that its trying to invoke the Context ctx = new InitialContext(); method in original method(someMethod).

Handmade
As InitialContext doc says, you can provide your own factory for InitialContext objects, using java.naming.factory.initial system property. When the code runs inside application server, the system property is set by the server. In our tests, we provide our own implementation of JNDI.
Here's my Mockito only solution: I defined a custom InitialContextFactory class, that returns a mock of InitialContext. You customize the mock as you wish, probably to return more mocks on lookup calls.
public class PlainTest {
#Mock InitialContextFactory ctx;
#InjectMocks Klasa1 klasa1;
public static class MyContextFactory implements InitialContextFactory
{
#Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
ConnectionFactory mockConnFact = mock(ConnectionFactory.class);
InitialContext mockCtx = mock(InitialContext.class);
when(mockCtx.lookup("jms1")).thenReturn(mockConnFact);
return mockCtx;
}
}
#Before
public void setupClass() throws IOException
{
MockitoAnnotations.initMocks(this);
System.setProperty("java.naming.factory.initial",
this.getClass().getCanonicalName() + "$MyContextFactory");
}
Spring (added by edit)
If you don't mind leveraging Spring Framework for testing purposes, here's their simple solution: SimpleNamingContextBuilder:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
DataSource ds = new DriverManagerDataSource(...);
builder.bind("java:comp/env/jdbc/myds", ds);
builder.activate();
It's ok to put it in #Before or #BeforeClass. After activate(), jndi data will be pulled from spring dummy.

Adding to Jarekczek's answer (Thanks for it!!). Though it is an old question I would like to share my version of it in case it helps someone. I faced the same problem and one might just want to mock IntialContext only in a IntialContextFactory implementation class and it would be a better idea to use this mocked object in other tests or base test classes to avoid duplication.
public class MyContextFactory implements InitialContextFactory {
// Poor Singleton approach. Not thread-safe (but hope you get the idea)
private static InitialContext mockInitialContext;
#Override
public Context getInitialContext(Hashtable<?,?> hshtbl) throws NamingException {
if(mockInitialContext == null) {
mockInitialContext = mock(InitialContext.class);
}
return mockInitialContext;
}
}
public class TestClass {
private DataSource mockDataSource;
private Connection mockConnection;
protected void mockInitialContext() throws NamingException, SQLException {
System.setProperty("java.naming.factory.initial", "com.wrapper.MyContextFactory");
InitialContext mockInitialContext = (InitialContext) NamingManager.getInitialContext(System.getProperties());
mockDataSource = mock(DataSource.class);
mockConnection = mock(Connection.class);
when(mockInitialContext.lookup(anyString())).thenReturn(mockDataSource);
when(mockDataSource.getConnection()).thenReturn(mockConnection);
try {
when(mockDataSource.getConnection()).thenReturn(mockConnection);
} catch (SQLException ex) {
Logger.getLogger(CLASSNAME).log(Level.SEVERE, null, ex);
}
}
}
Reason behind taking this approach being if someone wants to use DataSource or any other resource provided by JNDI in a different way for different tests, you can follow this approach. There shall be just one instance created for IntialContext unless a multi-threaded test tries to access it simultaneously (don't know why would one try to do that!). That instance can be used in all places to get JNDI objects you want and use them as you want.
Hope this helps!
"Make sure you wash your hands before every meal and avoid System.out.println while debugging for healthy lifestyle"

You can refactor your code and extract the initialization of the context in new method.
private void someMethod(int id1, int id2, HashMap map){
......some code........
Context ctx = getInitialContext();
Object ref = ctx.lookup("com.java.ejbs.MyEJB");
EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
ejbBean.someMethod(id1,name);
.......some code.......}
Your test code will be something like this:
Context mockContext = mock(Context.class);
doReturn(mockContext).when(yourclass).getInitalContext();
...... some code....

As of now (PowerMock 1.7.4)
Create a mock using PowerMockito.mock(InitialContext.class) rather than PowerMockito.createMock(InitialContext.class)
#Test
public void connectTest() {
String jndi = "jndi";
InitialContext initialContextMock = PowerMockito.mock(InitialContext.class);
ConnectionFactory connectionFactoryMock = PowerMockito.mock(ConnectionFactory.class);
PowerMockito.whenNew(InitialContext.class).withNoArguments().thenReturn(initialContextMock);
when(initialContextMock.lookup(jndi)).thenReturn(connectionFactoryMock);
...
// Your asserts go here ...
}
Do not create the InitialContext manually but let PowerMock do it for you. Also do not create a spy in which PowerMock expects an object. This means that you need to create the InitialContext instance.

Define the following Custom Classes
public static class CustomInitialContext extends InitialContext {
Hashtable<String, Object> ic = new Hashtable<>();
public CustomInitialContext() throws NamingException {
super(true);
}
public void bind(String name, Object object) {
ic.put(name, object);
}
public Object lookup(String name) throws NamingException {
return ic.get(name);
}
}
public static class CustomInitialContextFactory implements InitialContextFactory {
static InitialContext ic;
public CustomInitialContextFactory() {
if (ic == null) {
try {
ic = new CustomInitialContext();
} catch (NamingException e) {
e.printStackTrace();
}
}
}
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return ic;
}
}
public static class CustomInitialContextFactoryBuilder implements InitialContextFactoryBuilder {
#Override
public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
return new CustomInitialContextFactory();
}
}
and declare factory as
NamingManager.setInitialContextFactoryBuilder(new CustomInitialContextFactoryBuilder());

Related

JNDI lookup of EJB with new InitialContext each time?

We use EJB3 and JBOSS application Server in our Application. I have a Bean lookup utility method where its a generic method written to lookup stateless EJB Beans by JNDI name:
public class BeanFactory {
static Logger logger = LogManager.getLogger(BeanFactory.class.getName());
/**
*
* #param jndiName
* #return
*/
public static <T> T lookup(String jndiName){
logger.info("Inside bean BeanFactory lookup: " + jndiName);
T handle = null;
try {
InitialContext ctx = new InitialContext();
handle = (T) ctx.lookup(jndiName);
} catch (Exception e) {
logger.error(e, e.fillInStackTrace());
}
return handle;
}
So there are classes which have dependencies on Beans and they use lookup method to invoke the methods of the Bean. For Example
private AuthenticationProfileDTO getAuthenticationProfile(String credId) throws OneM2MException {
ResourceProceduresDao dao = BeanFactory.lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = BeanFactory.lookup(AuthenticationProfileRemote.JNDI_NAME);
AuthenticationProfileDTO authenticationProfileDTO;
if (isKpsaId(credId))
authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForSymmKeyID(credId);
else
authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForCredentialID(credId);
return authenticationProfileDTO;
}
So now when we ran JProfiler on the code the lookup method is coming to be time consuming because every time lookup is called a new InitialContext is instantiated.
I was thinking of making the InitialContext static so that only once it's initialized in a static block, but I don't know what implications will it have in terms of getting Bean instances. Since this piece of code is managed by EJB Container, the run time impacts are unknown. After looking up some articles online not much clarity was there.
Any help is appreciated.
Note that javadoc for InitialContext warns that:
An InitialContext instance is not synchronized against concurrent
access by multiple threads. Multiple threads each manipulating a
different InitialContext instance need not synchronize.
Threads that need to access a single InitialContext instance
concurrently should synchronize amongst themselves and provide the
necessary locking.
So, making the field static isn't necessarily a good idea as you'll need to synchronize each lookup(jndiName) call, and this may cause other issues as per comment by James R. Perkins.
However as you have shown that getAuthenticationProfile(String credId) calls lookup twice, there is no reason why you can't make a BeanFactory hold one InitialContext to reduce the number of instances by re-using InitialContext within same calling methods.
public class BeanFactory {
private final InitialContext ctx;
private BeanFactory(InitialContext initialContext) {
this.ctx = initialContext;
}
private static final Logger logger = LogManager.getLogger(BeanFactory.class.getName());
/** JNDI lookup*/
public <T> T lookup(String jndiName){
// logger.info("Inside bean BeanFactory lookup: " + jndiName);
try {
return (T) ctx.lookup(jndiName);
} catch (Exception e) {
RuntimeException re = new RuntimeException("Could not find jndi: "+jndiName, e);
logger.error(re);
throw re;
}
}
/** Setup a new BeanFactory */
public static BeanFactory create() {
try {
return new BeanFactory(new InitialContext());
} catch (Exception e) {
throw new RuntimeException("Could not create a new context", e);
logger.error(re);
throw re;
}
}
This allows getAuthenticationProfile to use a single InitialContext for 2 lookups:
BeanFactory ctx = BeanFactory.create();
ResourceProceduresDao dao = ctx.lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = ctx.lookup(AuthenticationProfileRemote.JNDI_NAME);
You might also consider whether saving BeanFactory as a thread local would help though I would be very concerned about doing this an application server because you may have little control over which and how many threads instantiate InitialContext and what from what context they run. However it might be suitable within a standalone client program accessing your EJB server logic:
private static final ThreadLocal<BeanFactory> BEANS = ThreadLocal.withInitial(BeanFactory::create);
private static BeanFactory local() {
return BEANS.get();
}
// Example lookups:
ResourceProceduresDao dao = BeanFactory.local().lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = BeanFactory.local().lookup(AuthenticationProfileRemote.JNDI_NAME);

Unable to mock the same class constructor two times with different behaviors in Junit5 Mockito-inline [duplicate]

I have a legacy class that contains a new() call to instantiate a LoginContext object:
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
}
}
I want to test this class using Mockito to mock the LoginContext as it requires that the JAAS security stuff be set up before instantiating, but I'm not sure how to do that without changing the login() method to externalize the LoginContext.
Is it possible using Mockito to mock the LoginContext class?
For the future I would recommend Eran Harel's answer (refactoring moving new to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:
You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).
Real spies should be used carefully and occasionally, for example when dealing with legacy code.
In your case you should write:
TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);
I am all for Eran Harel's solution and in cases where it isn't possible, Tomasz Nurkiewicz's suggestion for spying is excellent. However, it's worth noting that there are situations where neither would apply. E.g. if the login method was a bit "beefier":
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
... and this was old code that could not be refactored to extract the initialization of a new LoginContext to its own method and apply one of the aforementioned solutions.
For completeness' sake, it's worth mentioning a third technique - using PowerMock to inject the mock object when the new operator is called. PowerMock isn't a silver bullet, though. It works by applying byte-code manipulation on the classes it mocks, which could be dodgy practice if the tested classes employ byte code manipulation or reflection and at least from my personal experience, has been known to introduce a performance hit to the test. Then again, if there are no other options, the only option must be the good option:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
TestedClass tc = new TestedClass();
tc.login ("something", "something else");
// test the login's logic
}
}
EDIT:
Modern versions of Mockito provide similar functionality without needing the extra PowerMock library with the mockito-inline dependency (instead of the mockito-core dependency):
public class TestedClassTest {
#Test
public void testLogin() {
try (MockedConstruction<LoginContext> mockedConstruction =
Mockito.mockConstruction(LoginContext.class)) {
TestedClass tc = new TestedClass();
tc.login("something", "something else");
// test the login's logic
}
}
}
You can use a factory to create the login context. Then you can mock the factory and return whatever you want for your test.
public class TestedClass {
private final LoginContextFactory loginContextFactory;
public TestedClass(final LoginContextFactory loginContextFactory) {
this.loginContextFactory = loginContextFactory;
}
public LoginContext login(String user, String password) {
LoginContext lc = loginContextFactory.createLoginContext();
}
}
public interface LoginContextFactory {
public LoginContext createLoginContext();
}
public class TestedClass {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
}
}
-- Test Class:
#RunWith(PowerMockRunner.class)
#PrepareForTest(TestedClass.class)
public class TestedClassTest {
#Test
public void testLogin() {
LoginContext lcMock = mock(LoginContext.class);
whenNew(LoginContext.class).withArguments(anyString(), anyString()).thenReturn(lcMock);
//comment: this is giving mock object ( lcMock )
TestedClass tc = new TestedClass();
tc.login ("something", "something else"); /// testing this method.
// test the login's logic
}
}
When calling the actual method tc.login ("something", "something else"); from the testLogin() {
- This LoginContext lc is set to null and throwing NPE while calling lc.doThis();
Not that I know of, but what about doing something like this when you create an instance of TestedClass that you want to test:
TestedClass toTest = new TestedClass() {
public LoginContext login(String user, String password) {
//return mocked LoginContext
}
};
Another option would be to use Mockito to create an instance of TestedClass and let the mocked instance return a LoginContext.
In situations where the class under test can be modified and when it's desirable to avoid byte code manipulation, to keep things fast or to minimise third party dependencies, here is my take on the use of a factory to extract the new operation.
public class TestedClass {
interface PojoFactory { Pojo getNewPojo(); }
private final PojoFactory factory;
/** For use in production - nothing needs to change. */
public TestedClass() {
this.factory = new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
}
/** For use in testing - provide a pojo factory. */
public TestedClass(PojoFactory factory) {
this.factory = factory;
}
public void doSomething() {
Pojo pojo = this.factory.getNewPojo();
anythingCouldHappen(pojo);
}
}
With this in place, your testing, asserts and verify calls on the Pojo object are easy:
public void testSomething() {
Pojo testPojo = new Pojo();
TestedClass target = new TestedClass(new TestedClass.PojoFactory() {
#Override
public Pojo getNewPojo() {
return testPojo;
}
});
target.doSomething();
assertThat(testPojo.isLifeStillBeautiful(), is(true));
}
The only downside to this approach potentially arises if TestClass has multiple constructors which you'd have to duplicate with the extra parameter.
For SOLID reasons you'd probably want to put the PojoFactory interface onto the Pojo class instead, and the production factory as well.
public class Pojo {
interface PojoFactory { Pojo getNewPojo(); }
public static final PojoFactory productionFactory =
new PojoFactory() {
#Override
public Pojo getNewPojo() {
return new Pojo();
}
};
I happened to be in a particular situation where my usecase resembled the one of Mureinik but I ended-up using the solution of Tomasz Nurkiewicz.
Here is how:
class TestedClass extends AARRGGHH {
public LoginContext login(String user, String password) {
LoginContext lc = new LoginContext("login", callbackHandler);
lc.doThis();
lc.doThat();
return lc;
}
}
Now, PowerMockRunner failed to initialize TestedClass because it extends AARRGGHH, which in turn does more contextual initialization... You see where this path was leading me: I would have needed to mock on several layers. Clearly a HUGE smell.
I found a nice hack with minimal refactoring of TestedClass: I created a small method
LoginContext initLoginContext(String login, CallbackHandler callbackHandler) {
new lc = new LoginContext(login, callbackHandler);
}
The scope of this method is necessarily package.
Then your test stub will look like:
LoginContext lcMock = mock(LoginContext.class)
TestedClass testClass = spy(new TestedClass(withAllNeededArgs))
doReturn(lcMock)
.when(testClass)
.initLoginContext("login", callbackHandler)
and the trick is done...

NullPointerException while testing Spring boot camel application

public class MyRouteTest extends CamelSpringTestSupport {
#Override
protected AbstractApplicationContext createApplicationContext() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.mypackage.routes");
ctx.refresh();
return ctx;
}
#Test
public void testRouteRunning() throws Exception {
assertTrue(context().getRouteStatus("direct:start").isStarted());
}
}
getRouteStatus is returning null I am following this to write my test cases
Any pointers on how to fix this will be very helpful.
You need to use the id of the route, not the url, eg "direct:start" is the url of the route, the route has an id as well. If you do not specify an id, then an id is auto assigned such as route1, route2 etc.
Use .routeId("myNameHere") to specify the id of a route.

Inject based on the package of the class

I have 2 modules containing classes:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
All these DAOs have a dependency on the same service, but I need to inject a different instance based on the package.
I mean the module blog should have a specific instance of MyService, and the module users should have another instance of MyService.
I don't want to create 2 named services because some day I may want to use the same service for all DAOs. Or I could also want to inject another specific instance for a specific class...
Is there a way to inject a service based on the package of a class?
A way to say:
inject foo (instance of MyService) into classes that are in blog.*
inject bar (instance of MyService) into classes that are in users.*
but keeping all my classes unaware of that! Their configuration should only state "Inject an instance of MyService".
First I want to say, I find this a strange requirement. I am also wondering why your DAOs need a Service. In a normal layered design, this is the opposite (the Service uses the DAO).
However I find the challenge interesting, I tried to use a FactoryBean to create a Java Proxy class which would redirect at runtime to the correct instance of MyService depending of the caller package. Here is the code:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
I didn't had to change anything to the Dao or Service configuration. I just had to add the creation of the FactoryBean in the Spring context:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
Maybe a few comments:
The caller package can only be get by creating an exception and looking at the stacktrace.
The code of the InvocationHandler is inspired from ServiceLocatorFactoryBean.
I am still wondering if there is an easier way but I think there is not.
You could replace part of the InvocationHandler to use a configuration Map (package => MyService bean name)
I would not recommend using such code in a productive environment.

How to fake InitialContext with default constructor

All,
I'm trying to do some unit testing in some archaic java code (no interfaces, no abstraction, etc.)
This is a servlet that uses a ServletContext (which I'm assuming is set up by Tomcat) and it has database information is set up in the web.xml/context.xml file. Now, I've figured out how to make a Fake ServletContext, but the code has
InitialContext _ic = new InitialContext();
all over the place (so it isn't feasible to replace it). I need to find a way to make a default InitialContext() able to do the _ic.lookup(val) without throwing an exception.
I'm assuming there is some way that the context.xml is getting loaded, but how that magic works, I'm drawing a blank. Anyone have any ideas?
Take advantage of the fact that InitialContext uses an SPI to handle its creation. You can hook into its lifecycle by creating an implementation of javax.naming.spi.InitialContextFactory and passing that to your tests via the system property javax.naming.factory.initial (Context.INTITIAL_CONTEXT_FACTORY). It's simpler than it sounds.
Given this class:
public class UseInitialContext {
public UseInitialContext() {
try {
InitialContext ic = new InitialContext();
Object myObject = ic.lookup("myObject");
System.out.println(myObject);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
And this impl of InitialContextFactory:
public class MyInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0)
throws NamingException {
Context context = Mockito.mock(Context.class);
Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!");
return context;
}
}
Creating an instance of UseInitialContext in a junit test with
-Djava.naming.initial.factory=initial.context.test.MyInitialContext
on the command line outputs This is my object!! (easy to set up in eclipse). I like Mockito for mocking and stubbing. I'd also recommend Micheal Feather's Working Effectively with Legacy Code if you deal with lots of legacy code. It's all about how to find seams in programs in order to isolate specific pieces for testing.
Here's my solution to setting up the Inintial Context for my unit tests. First I added the following test dependency to my project:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.33</version>
<scope>test</scope>
</dependency>
Then I created a static method with the following code:
public static void setupInitialContext() throws Exception {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("jdbc");
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setDatabaseName("postgres");
ds.setUser("postgres");
ds.setPassword("admin");
ic.bind("jdbc/something", ds);
}
Finally in each of my test class I add an #BeforeClass method which calls setupInitialContext.
Try setting up the system variables before:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.apache.naming");
InitialContext ic = new InitialContext();
If you are using JUnit, follow this doc: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit
You can use PowerMock to mock construction of the InitialContext and control its behavior. Constructor Mocking is documented here.
PowerMock tests can be quite messy and complicated, refactoring is normally a better option.
Today I've faced the same problem (we can't user PowerMock) and solved it this way:
Don't lookup in the constructor so when you invoke #InitMock on the object, the constructor doesn't require the context yet.
Create a method for retrieving the service bean when needed like "getService().serviceMethod(param, param ...)":
/* Class ApplicationResourceProvider */
/* We can mock this and set it up with InjectMocks */
InitialContext ic;
/* method hiding the lookup */
protected ApplicationService getService() throws NamingException {
if(ic == null)
ic = new InitialContext();
return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal");
}
On the test, set it up:
#Mock
ApplicationService applicationServiceBean;
#Mock
InitialContext ic;
#InjectMocks
ApplicationResourceProvider arp;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(ic.lookup(anyString())).thenReturn(applicationServiceBean);
...
}
Have you considered mockito?
It's as easy as:
InitialContext ctx = mock(InitialContext.class);
By the way, should you choose to use mocks i would recommend reading this article as well: http://martinfowler.com/articles/mocksArentStubs.html
A poor man's standalone implementation using no external libraries:
public class myTestClass {
public static class TestContext extends InitialContext {
public TestContext() throws NamingException {
super(true /*prevents initialization*/);
}
static Object someExpectedValue = "the expected string or object instance";
/*override the method(s) called by the legacy program on _ic, check the parameter and return the wanted value */
public Object lookup(String name) throws NamingException {
return name != null && name.equals("theValueOfVal") ? someExpectedValue : null;
}
}
public static class TestInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return new TestContext();
}
}
public static void main(String[] args) throws SQLException {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "the.package.myTestClass$TestInitialContextFactory");
/*now call the legacy logic to be tested*/
...
You could use a switch in the override of the lookup method to return the expected value for each different val value passed to _ic.lookup(val) throughout the legacy program.

Categories