1
votes

I have a open source project hosted at this github. I am facing a weird scenario. I found many people who could make @Autowired Spring annotation work on their main classes but not on their JUnit test classes. My problem is the other way around. I can successfully use @Autowired in my JUnit test class but when a test calls my main class, the dependencies don't get injected there. Here is my context (simplified version):

Login Class:

package net.openrally.restaurant.core.exposure.resource;

@Path("/login")
@Component
@Transactional
@Singleton
@Produces("application/json")
@Consumes("application/json")
public class Login extends BaseResource{

    @Autowired
    private UserDAO userDAO;

    @POST
    public Response post(String requestBody){

        ...

        //NullPointerException
        User user = userDAO.loadByCompanyIdAndLogin(companyId, login);
    }

    ...

}

LoginTest Class:

package net.openrally.restaurant.core.exposure.resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class LoginTest extends BaseResourceTest {

    @Autowired
    private UserDAO userDAO;

    ...

    @Test
    public void testInvalidPassword() {

    ....

    // Works perfectly!
    userDAO.save(user);

    ....

    }

}

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:configuration.properties</value>
            </list>
        </property>
    </bean>

    <context:annotation-config />
    <context:component-scan base-package="net.openrally.restaurant.core" />

    <import resource="db-config.xml" />
</beans>

I am searching for a solution for a few days now. From what I could find so far, most of the people who have @Autowired problems dont have either <context:annotation-config /> or xmlns:context="http://www.springframework.org/schema/context" in their spring xml or dont have a @Component family annotation in the class they want DI to take place, and as you can see, they are both there :(. I have only one applicationContext.xml in my project which suits for both runtime and test (I have distinct configuration.properties to set database credentials and log levels differently, but no spring configuration there)

I'm using:

Spring: 3.1.0.RELEASE
JUnit: 4.10
Jersey: 1.11
CLIB: 2.2.2

Any ideas, and I mean ANY :), is much appreciated.

UPDATE

The following logs come up when I run the test:

2012-03-27 07:37:02,457 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'login'
2012-03-27 07:37:02,457 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'login'
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Found injected element on class [net.openrally.restaurant.core.exposure.resource.Login]: AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.ConfigurationDAO net.openrally.restaurant.core.exposure.resource.Login.configurationDAO
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Found injected element on class [net.openrally.restaurant.core.exposure.resource.Login]: AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.UserDAO net.openrally.restaurant.core.exposure.resource.Login.userDAO
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'login' to allow for resolving potential circular references
2012-03-27 07:37:02,459 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Getting BeanInfo for class [net.openrally.restaurant.core.exposure.resource.Login]
2012-03-27 07:37:02,462 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Caching PropertyDescriptors for class [net.openrally.restaurant.core.exposure.resource.Login]
2012-03-27 07:37:02,462 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Found bean property 'class' of type [java.lang.Class]
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'login': AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.ConfigurationDAO net.openrally.restaurant.core.exposure.resource.Login.configurationDAO
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'configurationDAO'
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'login' to bean named 'configurationDAO'
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'login': AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.UserDAO net.openrally.restaurant.core.exposure.resource.Login.userDAO
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userDAO'
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'login' to bean named 'userDAO'
2012-03-27 07:37:02,464 DEBUG [main] org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator - Creating implicit proxy for bean 'login' with 0 common interceptors and 1 specific interceptors
2012-03-27 07:37:02,464 DEBUG [main] org.springframework.aop.framework.Cglib2AopProxy - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [net.openrally.restaurant.core.exposure.resource.Login@60532a0a]
2012-03-27 07:37:02,465 DEBUG [main] org.springframework.aop.framework.Cglib2AopProxy - Unable to apply any optimisations to advised method: public javax.ws.rs.core.Response net.openrally.restaurant.core.exposure.resource.Login.post(java.lang.String)

Could Spring be creating my bean correctly but Jersey using its own non-autowired instance?

2
Is there any sort of conflict between @Component and @Singleton on your bean ? And how is loaded your applicationContext.xml file for runtime ? - ndeverge
Hello nico_ekito, thanks for your input. The @Singleton is just an optimization. I tried removing it after your comment but no luck... The appliationContext.xml is in the resources folder and project has spring dependencies controlled by maven. I plan to use jetty as a web container but for the tests I am using Grizzly Web Container from Jersey. - robertobado
But how your applicationContext.xml file is loaded in your runtime app ? By a ClassPathXmlApplicationContext ? By a webapp ? - ndeverge
Currently. my applicationContext.xml is loaded by junit tests bootstrap. For production it will be loaded by the web container. I didn't get much in detail regarding to that... How could I check if it is being loaded properly? Looks seem to indicate so... - robertobado
If you do not use any context loader, it will not load. In a web application, use a ContextLoaderListener : static.springsource.org/spring/docs/2.5.x/reference/… - ndeverge

2 Answers

0
votes

After researching a while, I could not find a way to make GrizzlyWeb use the context loaded by spring, so I tried to find an alternative solution.

I then found Hifaces20, which will let you start and stop a jetty instance, within the same JVM (which means you may, for instance, use a memory database that will be seen both by your tests and your application)

0
votes

I had the exact same issue and later found that it was a developer issue :)

I was creating a new object instead of using the spring bean.

Lets say class MyService autowires a dao class MyDao myDaoBean. Now lets say I want to use MyService in MyController, I should wire in the Spring bean myServiceSpringBean. If I try to create a new myServiceObject then Spring doesn't wire the myDaoBean into the myServiceObject because it's not aware about that new service object.

And that leads to myDaoBean being null.