Spring unit test objects autowired with null fields - java

I'm attempting to create unit tests for a rest service in my spring servlet. But when the controller object is created by #autowire, then all of its #autowired fields are null.
My test class is as follows, using the SpringJUnit runner and context configuration set
#ContextConfiguration(locations = "ExampleRestControllerTest-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class ExampleRestControllerTest {
#Autowired
private BaseService mockExampleService;
#Autowired
private ExampleRestController testExampleRestController;
The ExampleRestControllerTest-context.xml sets up the service to be mocked and injects the mocked object into the controller
<context:annotation-config/>
<import resource="classpath*:example-servlet.xml"/>
<bean id="mockExampleService" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg index="0" value="za.co.myexample.example.services.BaseService"/>
</bean>
<bean id="testExampleRestController" class="za.co.myexample.example.rest.controller.ExampleRestController">
<property name="exampleService" ref="mockExampleService"/>
</bean>
The rest of the beans used by the controler are defined in the example-servlet.xml
<bean id="RESTCodeMapper" class="za.co.myexample.example.rest.util.RESTCodeMapper"/>
<bean id="restProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:RESTServiceCodeMapping.properties"/>
</bean>
Along with my Jax2BMarshaller.
My IDE links to these definitions just fine and if I remove any of the definitions I get an "No qualifying bean" error as expected.
My problem is that when I run my unit tests the controller that is provided has all of its fields as nulls
#Controller
public abstract class BaseRestController {
private static Logger LOGGER = Logger.getLogger(BaseRestController.class);
protected final String HEADERS = "Content-Type=application/json,application/xml";
#Autowired
protected RESTCodeMapper restCodeMapper;
#Autowired
protected BaseService exampleService;
#Autowired
protected Jaxb2Marshaller jaxb2Marshaller;
(and its implementing class)
#Controller
#RequestMapping("/example")
public class ExampleRestController extends BaseRestController {
When I run the proper code in my Weblogic server the fields get properly populated. More so if I add these fields in my test class directly those fields also get #autowired correctly. I could thus #autowire those objects in my test class and then set them directly into my controller. But that's not the right way of doing it.
So the question is, why are the autowired fields of an autowired object null in my test class when it autowires those objects directly in the same test class or if I run the code normally on my Weblogic server.

In your test class, the #Autowired objects are null because of context configuration, so change it to:
#ContextConfiguration(locations = "ExampleRestControllerTest-context.xml")
to
#ContextConfiguration(locations = "classpath:/ExampleRestControllerTest-context.xml")
in ExampleRestControllerTest
So the question is, why are the autowired fields of an autowired
object null in my test class when it autowires those objects directly
in the same test class or if I run the code normally on my Weblogic
server.
They must be null in autowired objects' constructor. You can try to create #PostConstruct method in autowired object and in this method the autowired objects must be not null

Related

XML bean definitions don't pick up #Component annotated beans in dependency packages

Not sure what i'm missing here but some assistance would be appreciated.
I have a class annotated with #Component as follows in a package com.company.package:
#Component
public class ClassAService {
#Autowired
public ClassAService(ClassB classB) {
....
}
}
Within the same package, I have a second class that utilizes ClassAService in its constructor argument as follows. Note that this class is not annotated with #Component
public class ClassBService {
String id;
#Autowired
public ClassBService(ClassAService classAService) {
....
}
}
Finally, in the main Application that utilizes the package described above, i have the following xml configuration that utilizes ClassBService to create a bean definition as follows:
....
<bean id="classBService" class="com.company.package.ClassBService">
<property name="id" value="abcde" />
</bean>
Finally, i have annotated my run application class with the following:
#SpringBootApplication({
scanBasePackages= {
"com.company.package.*"
})
Given everything above, I would expect the bean defined in the xml file to inject ClassAService correctly and construct the bean. However, I keep getting the following exception
Parameter 0 of constructor in com.company.package.ClassBService required a bean of type 'com.company.package.ClassAService' that could not be found.
However, I have no such issues when I use ClassAService directly in code using #Autowire:
#Service
public class MainService {
#Autowire
public ClassAService classAService
}
It shouldn't make a difference how the beans are declared as they are stored in the application context when the app fully boots up. So what the issue here?

Spring Prototype-Bean Provider without #Autowired

I have a prototype Bean which is instantiated by singleton bean with a Provider:
#Component
#Scope("prototype")
class MyPrototype {}
#Component
class MySingleton {
#Autowired
javax.inject.Provider<MyPrototype> prototypeFactory;
}
This works fine, but our company rules state that #Autowired is not allowed; the common pattern is #Resource(SingletonBeanClass.BEAN_ID).
Is it possible to annotate the Provider this way so the Spring lookup can create it?
I'm aware I can add a factory method with #Lookup, or a singleton factory bean, but I prefer the Provider.
EDIT:
I didn't get it to work this way and in the end had to edit spring.xml; see below for details.
As you have an XML configuration file, you can configure it via XML in the following way:
<bean id="myPrototype" class="some.package.MyPrototype" scope="prototype" />
<bean id="mySingleton" class="some.package.MySingleton">
<lookup-method name="getPrototypeFactory" bean="myPrototype "/>
</bean>
In this way, you have to access to the myPrototype with the getPrototypeFactory() and not directly to the property. You can even remove the annotations on those 2 classes.
For any extra details, you can look at the following blog post Injecting a prototype bean into a singleton bean
For reference, if someone comes across this via Google:
I ended up needing to declare it in the spring.xml. I tried #Lookup, but even that didn't work due to the prototype-bean referencing yet another prototype-bean.
This is how it was recommended here,
but it does not work:
#Component("proto1")
#Scope("prototype")
class MyPrototypeBean1 {
#Lookup(value="proto2")
protected MyPrototypeBean2 createBean2() { return null; }
}
#Component("proto2")
#Scope("prototype")
class MyPrototypeBean2 {
}
#Component("singleton")
class MySingleton {
#Lookup(value="proto1")
protected MyPrototypeBean1 createBean1() { return null; }
}
This results in the error message "Cannot apply #Lookup to beans without corresponding bean definition" when trying to create "innerBean...".
I assume it is due to "lookup methods cannot get replaced on beans returned from factory methods where we can't dynamically provide a subclass for them" as is quoted in the link above.
So what I ended up doing in the spring.xml:
<bean name="proto2" class="my.package.PrototypeBean2" />
<bean name="proto1" class="my.package.PrototypeBean1" >
<lookup-method name="createBean2" bean="proto2" />
</bean>
<bean name="singleton" class="my.package.SingletonBean" >
<lookup-method name="createBean1" bean="proto1" />
</bean>
And this works.
For the unit tests, I had to subclass the respective classes:
class SingletonUnitTest {
#Mock
MyPrototypeBean1 bean1;
#InjectMocks
DummySingleton sut;
#Before public void setBean1() {
sut.bean = bean1;
}
static class DummySingletonBean extends MySingeton {
MyPrototypeBean1 bean;
protected MyPrototypeBean1 createBean1() {
return bean;
}
}
}

Spring Test's MockMvc and Spring Remoting's HttpInvoker

I've got web application which is split into two parts
(being run in different jvms):
#RestController layer;
#Service layer (business and data access logic).
They communicate with each other via Spring Remoting:
(org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
on #RestController layer and
org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
on #Service layer).
These two parts are deployed on different application servers.
Mostly they are tested via Spring RestTemplate
(#Service part has to be deployed and started manually
and after that integration tests are run).
But as I used Spring Test and MockMvc in past and found it to be a great tool
I would like to use again and again.
Unfortunatelly I do not understand how can I add #Service layer context into test context configuration such way,
that it would be accessible from test (which holds #RestController context augmented with some mocks).
If I manually start application server with #Service layer artifacts (on localhost) and run mine MockMvc-driven test I can see that remote requests
from MockMvc get to their destination - #Service layer
(through httpInvoker of course).
And I want to find possibility to start #Service layer context within test context (with all needed HttpInvokerServiceExporters).
And to force httpInvoker to send its requests to this "pseudo" remote service (which in fact will be local).
Now I'm thinking about using embedded jetty for deploying #Service layer
and running MockMvc tests against this instance.
SpringHttpRemoting With EmbeddedJettyServer.wiki
I have very small experience in MicroService architecture
but it seems that mine situation is rather usual for it.
So maybe there are some more natural
(in spite of Spring Test and MockMvc particularly) ways for such testing ?
Thanks in advance.
Andrey.
Well, let me share my thoughts upon the matter.
Here is domain and api:
public class Contact implements Serializable {
private String firstName;
private String lastName;
private DateTime birthDate;
}
public interface ContactService {
List<String> getContacts();
}
#Service
public class ContactServiceImpl implements ContactService{
public List<String> getContacts() {
return new ArrayList<String>(asList("karl marx", " fridrih engels", " !!!"));
}
}
#RestController
public class ContactController {
public static final String QUALIFIER = "contactController";
public static final String MAPPING = "/contact";
#Autowired
private ContactService serviceInvoker;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> findAll() {
List<String> strings = serviceInvoker.getContacts();
return new ResponseEntity<String>(Arrays.toString(strings.toArray()), HttpStatus.OK);
}
}
It is rather simple to test such configuration by providing test with 'invoking side' context (restcontroller in my case)
and 'invoked side' context (containing real services, not remote proxies). Just as with monolith application context.
Fast and easy approach. But in some cases not enough
(e.g. You have customized HttpInvokerProxyFactoryBean on one side and customized HttpInvokerServiceExporter on the other for some purposes).
You can override HttpInvokerProxyFactoryBean class making it NonRemote.
First, modify HttpInvokerServiceExporter by overriding some of its methods; it is just needed to make methods connected to RemoteInvocation and RemoteInvocationResult public.
public class OpenedHttpServiceExporter extends HttpInvokerServiceExporter {
#Override
public RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException {
return super.readRemoteInvocation(request);
}
.
.
.
etc...
}
Let it be OpenedHttpServiceExporter. Create bean descriptor in test/resources, import production beans definitions which You need in test into it
and add OpenedHttpServiceExporter bean with the same name as original HttpInvokerServiceExporter has - it is needed for overriding one with the other.
test context descriptor openedServiceExporter.xml (without beans element):
<import resource="classpath:spring/serviceExporter.xml"/>
<bean id="contactExporter" class="pmp.testingremoting.service.OpenedHttpServiceExporter">
<property name="service" ref="contactServiceImpl"/>
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService"/>
</bean>
And imported descriptor:
<context:annotation-config/>
<context:component-scan base-package="pmp.testingremoting.service">
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
</context:component-scan>
<bean name="contactExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="contactServiceImpl"/>
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService"/>
</bean>
Extend HttpInvokerProxyFactoryBean, make a bean of this subclass, autowire HttpInvokerServiceExporter field into it.
Override
public Object invoke(MethodInvocation methodInvocation)
by calling OpenedHttpServiceExporter.invoke(createRemoteInvocation(methodInvocation), exporter.getService());
in it.
public class NonRemoteInvoker extends HttpInvokerProxyFactoryBean {
#Autowired
private OpenedHttpServiceExporter exporter;
public void setExporter(OpenedHttpServiceExporter exporter) {
this.exporter = exporter;
}
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return exporter.invoke(createRemoteInvocation(methodInvocation), exporter.getService());
}
}
Let's call this new class NonRemoteInvoker. It has to override only one method of super class and will serve as a bridge from 'invoking side' context to 'invoked side' context.
Create 'invoking side' test context descriptor (nonRemoteInvokerContext.xml) with an instance of NonRemoteInvoker
(again, with same name as original HttpInvokerProxyFactoryBean has; also for overriding).
<import resource="classpath:spring/webContext.xml"/>
<bean id="serviceInvoker" class="pmp.testingremoting.controller.NonRemoteInvoker">
<property name="serviceUrl" value="http://localhost:8080/remote/ContactService" />
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService" />
</bean>
and webContext.xml is
<mvc:annotation-driven/>
<context:annotation-config/>
<context:component-scan base-package="pmp.testingremoting.controller">
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
</context:component-scan>
Create test configuration for 'invoked side'. I used static #Configuration class and just imported test context descriptor into it.
#Configuration
#ImportResource(locations = {
"classpath:openedServiceExporter.xml"
})
static class TunedBusinessConfig {
}
Create test configuration for 'invoking side'. I did it the same way.
#Configuration
#ImportResource(locations = {
"classpath:nonRemoteInvokerContext.xml",
})
static class TunedRemoteInvokerConfig {
}
Now the test class. It will be marked via #WebAppConfiguration and have such #ContextHierarchy, that will allow 'invoking side' context to use 'invoked side' context (invoked - parent, invoking - child).
It is needed for injecting OpenedHttpServiceExporter into NonRemoteInvoker.
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextHierarchy({
#ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedBusinessConfig.class
}),
#ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedRemoteInvokerConfig.class
})
})
public class ContactControllerIntegrationTest {
.
.
.
}
This approach allowed me to cover in test not only rest and service layers logic, but also logic of customized RemoteInvoker (let's call it transport logic).
Here are more details:
https://github.com/PmPozitron/TestingRemoting/tree/lightVersion
I am not sure whether such approach is correct, so will not mark answer as accepted until appropriate moment.

Spring Bean Injection Failing Due To Proxy

Spring Version: 3.2.4.RELEASE and 3.2.9.RELEASE
Mockito Version: 1.8.5
I've been trying to introduce H2 tests to an old project for integration testing, and I've been running into a few issues. Due to the way transactions were propagating, I needed to mock out an autowired class. I've done this before, but I'm now running into severe problems. The following error message is being thrown when initialising the test:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.stuff.XMLITCase': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'TheProcessor' must be of type [com.stuff.XMLBatchFileProcessor], but was actually of type [$Proxy118]
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:307)
Diving into this a bit deeper, it turns out the the bean is in-fact a proxy. If we check the AbstractBeanFactory (round line 239), we can see the proxy:
sharedInstance = {$Proxy117#7035} "com.stuff.XMLBatchFileProcessor#66c540d0"
h = {org.springframework.aop.framework.JdkDynamicAopProxy#7039}
The only problem is, I've no clue where this is coming from. I've gone over the config and dependencies, and can't find anywhere that this should be happening.
Project Setup
Unfortunately I can't give a sample project for this, but I'll go over my test configuration. I have a root class that I extend for the tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/spring/spring-test-context.xml"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
public abstract class AbstractIntegrationTest {
}
This simply loads in some spring config and rolls back the transactions after each test.
The spring config is nothing strange either, though there is one difference between my other module and this one. This is the transaction manager and session factory:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>
<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
...
</bean>
In my other module, I'm using an entityManagerFactory, and a different transaction manager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
</bean>
The actually class has some autowired fields, and the usual #Service annotation:
#Service(value = "TheProcessor")
public final class XMLBatchFileProcessor extends BatchFileProcessor implements IXMLBatchProcessor {
Finally, the actual test is as follows:
public class XMLITCase extends AbstractIntegrationTest {
#Resource(name = "TheProcessor")
#InjectMocks
private XMLBatchFileProcessor xmlProcessor;
#Mock
private ProcessHelper processHelper;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void test() throws Exception {
Assert.assertNotNull(xmlProcessor);
}
}
If I replace the XMLBatchFileProcessor with the interface and autowire the field, then there aren't any problems compiling. Mockito, however, never replaces the autowired bean with the mocked object. If it did, then I wouldn't bother with the #Resource annotations and naming the service, thus avoiding the Proxy issue.
Any assistance on this would be appreciate. I'll be focusing on the session factory and the differences there, but it's quite possible that I'm missing something else entirely.
EDIT
Going on Sotirios' comment, I had another look this morning and indeed had missed that the xmlProcessor has a #Transactional annotation, thus meaning that the class needs to be proxied. If I remove the final declaration and let CGLib enhance it, then Mockito does replace the bean when initMocks(this) this called. When a method is called, however, CGLib seems to replace all the beans with Spring enhanced versions, hence overwriting the Mockito version.
What is the correct way to use both Mockito and Spring in an integration test for a class with #Transactional annotations?
Alright, once I realised that the class was being proxied due to the #Transactional annotation, the solution to the problem became clearer. What I needed to do was unwrap the proxy, and set the mocked object directly on that:
So in my AbstractIntegrationTest:
/**
* Checks if the given object is a proxy, and unwraps it if it is.
*
* #param bean The object to check
* #return The unwrapped object that was proxied, else the object
* #throws Exception
*/
public final Object unwrapProxy(Object bean) throws Exception {
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
Advised advised = (Advised) bean;
bean = advised.getTargetSource().getTarget();
}
return bean;
}
Then in my #Before:
#Mock
private ProcessHelper processHelper;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
IXMLBatchProcessor iXMLBatchProcessor = (IXMLBatchProcessor) unwrapProxy(xmlProcessor);
ReflectionTestUtils.setField(iXMLBatchProcessor , "processHelper", processHelper);
}
This left all the #Autowired classes intact, while injecting the correct mocked object.
you can optimise the accepted response using the class AopTestUtils that provides the methods:
getTargetObject to unwrap the top-level proxy if exists
getUltimateTargetObject to unwrap all levels of proxies if they
exist

#Autowired is not working with beans defined in application context file

I defined a bean in spring context file 'applicationContext.xml' like below :
<bean id="daoBean" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.xxx.DAOImpl" />
</bean>
In my service class (ServiceImpl), I am using this bean like below:
#Component("serviceImpl")
public class ServiceImpl{
// other code here
#Autowired
private transient DAOImpl daoBean;
// other code here
}
My service class is being accessed from my JUnit test class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/applicationContext.xml" })
public class JUnitTest{
// other code here
#Autowired
private transient ServiceImpl serviceImpl;
// test cases are here
}
When I execute the test case, it gives error saying:
Error creating bean with name 'ServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private transient com.xxx.DAOImpl
When I remove #Autowired from service class and use #Resource(name = "daoBean") the test case works fine.
public class ServiceImpl{
// other code here
#Resource(name = "daoBean")
private transient DAOImpl daoBean;
// other code here
}
My question is why #Autowired is not working in this case? Do I need to configure any thing else with #Autowired, so that it can work properly. I don't want to change my service layer classes to replace #Autowired to #Resource.
Mockito.mock() has a generic return type T which is erased at runtime, so Spring cannot infer the type of the created mock that will be simply registered as Object in the Spring context. That's why #Autowired doesn't work (as it tries to look up the dependency by its type).
Check out this answer for a solution to the problem.

Categories

Resources