Related
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());
I have several beans in my Spring context that have state, so I'd like to reset that state before/after unit tests.
My idea was to add a method to a helper class which just goes through all beans in the Spring context, checks for methods that are annotated with #Before or #After and invoke them.
How do I get a list of instantiated beans from the ApplicationContext?
Note: Solutions which simply iterate over all defined beans are useless because I have many lazy beans and some of them must not be instantiated because that would fail for some tests (i.e. I have a beans that need a java.sql.DataSource but the tests work because they don't need that bean).
For example:
public static List<Object> getInstantiatedSigletons(ApplicationContext ctx) {
List<Object> singletons = new ArrayList<Object>();
String[] all = ctx.getBeanDefinitionNames();
ConfigurableListableBeanFactory clbf = ((AbstractApplicationContext) ctx).getBeanFactory();
for (String name : all) {
Object s = clbf.getSingleton(name);
if (s != null)
singletons.add(s);
}
return singletons;
}
I had to improve it a little
#Resource
AbstractApplicationContext context;
#After
public void cleanup() {
resetAllMocks();
}
private void resetAllMocks() {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
for (String name : context.getBeanDefinitionNames()) {
Object bean = beanFactory.getSingleton(name);
if (Mockito.mockingDetails(bean).isMock()) {
Mockito.reset(bean);
}
}
}
I am not sure whether this will help you or not.
You need to create your own annotation eg. MyAnnot.
And place that annotation on the class which you want to get.
And then using following code you might get the instantiated bean.
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyAnnot.class));
for (BeanDefinition beanDefinition : scanner.findCandidateComponents("com.xxx.yyy")){
System.out.println(beanDefinition.getBeanClassName());
}
This way you can get all the beans having your custom annotation.
applicationContext.getBeanDefinitionNames() does not show the beans which are registered without BeanDefinition instance.
package io.velu.core;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan
public class Core {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Core.class);
String[] singletonNames = context.getDefaultListableBeanFactory().getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
}
}
Console Output
environment
systemProperties
systemEnvironment
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
messageSource
applicationEventMulticaster
lifecycleProcessor
As you can see in the output, environment, systemProperties, systemEnvironment beans will not be shown using context.getBeanDefinitionNames() method.
Spring Boot
For spring boot web applications, all the beans can be listed using the below endpoint.
#RestController
#RequestMapping("/list")
class ExportController {
#Autowired
private ApplicationContext applicationContext;
#GetMapping("/beans")
#ResponseStatus(value = HttpStatus.OK)
String[] registeredBeans() {
return printBeans();
}
private String[] printBeans() {
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
if (autowireCapableBeanFactory instanceof SingletonBeanRegistry) {
String[] singletonNames = ((SingletonBeanRegistry) autowireCapableBeanFactory).getSingletonNames();
for (String singleton : singletonNames) {
System.out.println(singleton);
}
return singletonNames;
}
return null;
}
}
[
"autoConfigurationReport",
"springApplicationArguments",
"springBootBanner",
"springBootLoggingSystem",
"environment",
"systemProperties",
"systemEnvironment",
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor",
"org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory",
"org.springframework.boot.autoconfigure.condition.BeanTypeRegistry",
"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry",
"propertySourcesPlaceholderConfigurer",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor.store",
"preserveErrorControllerTargetClassPostProcessor",
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor",
"org.springframework.context.annotation.internalRequiredAnnotationProcessor",
"org.springframework.context.annotation.internalCommonAnnotationProcessor",
"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor",
"org.springframework.scheduling.annotation.ProxyAsyncConfiguration",
"org.springframework.context.annotation.internalAsyncAnnotationProcessor",
"methodValidationPostProcessor",
"embeddedServletContainerCustomizerBeanPostProcessor",
"errorPageRegistrarBeanPostProcessor",
"messageSource",
"applicationEventMulticaster",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration$EmbeddedTomcat",
"tomcatEmbeddedServletContainerFactory",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration$TomcatWebSocketConfiguration",
"websocketContainerCustomizer",
"spring.http.encoding-org.springframework.boot.autoconfigure.web.HttpEncodingProperties",
"org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration",
"localeCharsetMappingsCustomizer",
"org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration",
"serverProperties",
"duplicateServerPropertiesDetector",
"spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration",
"conventionErrorViewResolver",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration",
"errorPageCustomizer",
"servletContext",
"contextParameters",
"contextAttributes",
"spring.mvc-org.springframework.boot.autoconfigure.web.WebMvcProperties",
"spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties",
"org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration",
"multipartConfigElement",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration$DispatcherServletConfiguration",
"dispatcherServlet",
"dispatcherServletRegistration",
"requestContextFilter",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration",
"hiddenHttpMethodFilter",
"httpPutFormContentFilter",
"characterEncodingFilter",
"org.springframework.context.event.internalEventListenerProcessor",
"org.springframework.context.event.internalEventListenerFactory",
"reportGeneratorApplication",
"exportController",
"exportService",
"org.springframework.boot.autoconfigure.AutoConfigurationPackages",
"org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration",
"spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties",
"standardJacksonObjectMapperBuilderCustomizer",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration",
"jsonComponentModule",
"jacksonObjectMapperBuilder",
"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration",
"jacksonObjectMapper",
"org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration",
"org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration",
"org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration",
"org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration",
"defaultValidator",
"org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration",
"error",
"beanNameViewResolver",
"errorAttributes",
"basicErrorController",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter",
"mvcContentNegotiationManager",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration",
"stringHttpMessageConverter",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration",
"mappingJackson2HttpMessageConverter",
"org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration",
"messageConverters",
"mvcConversionService",
"mvcValidator",
"requestMappingHandlerAdapter",
"mvcResourceUrlProvider",
"requestMappingHandlerMapping",
"mvcPathMatcher",
"mvcUrlPathHelper",
"viewControllerHandlerMapping",
"beanNameHandlerMapping",
"resourceHandlerMapping",
"defaultServletHandlerMapping",
"mvcUriComponentsContributor",
"httpRequestHandlerAdapter",
"simpleControllerHandlerAdapter",
"handlerExceptionResolver",
"mvcViewResolver",
"org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter$FaviconConfiguration",
"faviconRequestHandler",
"faviconHandlerMapping",
"defaultViewResolver",
"viewResolver",
"welcomePageHandlerMapping",
"org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration",
"objectNamingStrategy",
"mbeanServer",
"mbeanExporter",
"org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration",
"springApplicationAdminRegistrar",
"org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration",
"org.springframework.boot.autoconfigure.web.JacksonHttpMessageConvertersConfiguration",
"spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties",
"org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration",
"multipartResolver",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration$RestTemplateConfiguration",
"restTemplateBuilder",
"org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration",
"spring.devtools-org.springframework.boot.devtools.autoconfigure.DevToolsProperties",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$RestartConfiguration",
"fileSystemWatcherFactory",
"classPathRestartStrategy",
"classPathFileSystemWatcher",
"hateoasObjenesisCacheDisabler",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration$LiveReloadServerConfiguration",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LiveReloadConfiguration",
"optionalLiveReloadServer",
"org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration",
"lifecycleProcessor"
]
I've created a gist ApplicationContextAwareTestBase.
This helper class does two things:
It sets all internal fields to null. This allows Java to free memory that isn't used anymore. It's less useful with Spring (the Spring context still keeps references to all the beans), though.
It tries to find all methods annotated with #After in all beans in the context and invokes them after the test.
That way, you can easily reset state of your singletons / mocks without having to destroy / refresh the context.
Example: You have a mock DAO:
public void MockDao implements IDao {
private Map<Long, Foo> database = Maps.newHashMap();
#Override
public Foo byId( Long id ) { return database.get( id ) );
#Override
public void save( Foo foo ) { database.put( foo.getId(), foo ); }
#After
public void reset() { database.clear(); }
}
The annotation will make sure reset() will be called after each unit test to clean up the internal state.
Using the previous answers, I've updated this to use Java 8 Streams API:
#Inject
private ApplicationContext applicationContext;
#Before
public void resetMocks() {
ConfigurableListableBeanFactory beanFactory = ((AbstractApplicationContext) applicationContext).getBeanFactory();
Stream.of(applicationContext.getBeanDefinitionNames())
.map(n -> beanFactory.getSingleton(n))
// My ConfigurableListableBeanFactory isn't compiled for 1.8 so can't use method reference. If yours is, you can say
// .map(ConfigurableListableBeanFactory::getSingleton)
.filter(b -> Mockito.mockingDetails(b).isMock())
.forEach(Mockito::reset);
}
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.
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.
I have a property file and using Spring property place holder, I set values to the Spring beans. Now, this property file may be modified during the run time. Is there a way to refresh the properties of the Spring beans with this newly modified property value? Especially, I have many singleton beans? How can I refresh them with the new values? Is there already a solution to this or should it be custom coded? If it doesn't already exist, can someone please give the best approach to achieve this? Thanks!
PS: My application is a batch application. I use Spring based Quartz configuration to schedule the batches.
I'll leave this in for reference, but the updated answer is below the divider:
Well the ConfigurableApplicationContext interface contains a refresh() method, which should be what you want, but the question is: how to access that method. Whichever way you do it, you'll start with a bean that has a dependency of type ConfigurableApplicationContext:
private ConfigurableApplicationContext context;
#Autowired
public void setContext(ConfigurableApplicationContext ctx){
this.context = ctx;
}
Now the two basic options I'd suggest would be to either
use the Task Execution Framework and let your bean watch the property resources regularly, refreshing the ApplicationContext when it finds changes or
expose the bean to JMX, allowing you to manually trigger the refresh.
Referring to comments: since it seems impossible to refresh the entire context, an alternative strategy would be to create a properties factory bean and inject that into all other beans.
public class PropertiesFactoryBean implements FactoryBean<Properties>{
public void setPropertiesResource(Resource propertiesResource){
this.propertiesResource = propertiesResource;
}
private Properties value=null;
long lastChange = -1L;
private Resource propertiesResource;
#Override
public Properties getObject() throws Exception{
synchronized(this){
long resourceModification = propertiesResource.lastModified();
if(resourceModification != lastChange){
Properties newProps = new Properties();
InputStream is = propertiesResource.getInputStream();
try{
newProps.load(is);
} catch(IOException e){
throw e;
} finally{
IOUtils.closeQuietly(is);
}
value=newProps;
lastChange= resourceModification;
}
}
// you might want to return a defensive copy here
return value;
}
#Override
public Class<?> getObjectType(){
return Properties.class;
}
#Override
public boolean isSingleton(){
return false;
}
}
You could inject this properties bean into all your other beans, however, you would have to be careful to always use prototype scope. This is particularly tricky inside singleton beans, a solution can be found here.
If you don't want to inject lookup methods all over the place, you could also inject a PropertyProvider bean like this:
public class PropertiesProvider implements ApplicationContextAware{
private String propertyBeanName;
private ApplicationContext applicationContext;
public void setPropertyBeanName(final String propertyBeanName){
this.propertyBeanName = propertyBeanName;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException{
this.applicationContext = applicationContext;
}
public String getProperty(final String propertyName){
return ((Properties) applicationContext.getBean(propertyBeanName)).getProperty(propertyName);
}
}