tags:

views:

120

answers:

3

One of my integration tests uses multiple Spring context files. It seems that Spring only autowires in beans from the first context and not the second. Does anyone know what I am doing wrong or how to work around the problem?

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
        "classpath:/META-INF/spring/applicationContext.xml",
        "classpath:/META-INF/spring/applicationContext-security.xml"})
@Configurable
public class UserDetailsServiceImplIntegrationTest {

    @Autowired
    UserDataOnDemand dod;
    // @Autowired does not work for this bean from applicationContext-security.xml
    UserDetailsService userDetailsService;

    @Before
    public void setup() {
        dod.init();
        // workaround for autowiring problem
        userDetailsService = (UserDetailsService)ctx.getBean("userDetailsService");
    }

    @Test
    public void testLoadUser() {
        UserDetails ud = userDetailsService.loadUserByUsername("[email protected]");
        Assert.assertEquals("[email protected]", ud.getUsername());
    }
}

I am using Spring 3.0.3.

Here is the stack trace when I uncomment the @Autowired line for UserDetailsService:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'za.co.acme.app.security.UserDetailsServiceImplIntegrationTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.core.userdetails.UserDetailsService za.co.acme.app.security.UserDetailsServiceImplIntegrationTest.userDetailsService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [org.springframework.security.core.userdetails.UserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:286)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1064)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:374)
    at org.springframework.beans.factory.wiring.BeanConfigurerSupport.configureBean(BeanConfigurerSupport.java:140)
    at org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect.configureBean(AnnotationBeanConfigurerAspect.aj:59)
    at org.springframework.beans.factory.aspectj.AbstractDependencyInjectionAspect.ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(AbstractDependencyInjectionAspect.aj:89)
    at za.co.acme.app.security.UserDetailsServiceImplIntegrationTest.(UserDetailsServiceImplIntegrationTest.java:25)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.junit.runners.BlockJUnit4ClassRunner.createTest(BlockJUnit4ClassRunner.java:202)

The bean is definitely there since the "by name" lookup works, and it is of the correct type.

A: 

What is the name of the bean for userDetailsService in your xml? You might need to add a @Qualifier annotation with the bean name, and then putting a <qualifier> tag in the context.

Take a look at Spring's documentation on the topic.

Noel M
I did try that and also tried using @Resource without any luck. I only have one UserDetailsService bean so by type should also work. The error messages indicates that no bean with the correct type could be found.
David Tinker
+4  A: 

A workaround is to create a new singe configuration file (lets call ist "test-configuration.xml") which is including the both applicationContext.xml and applicationContext-security.xml. The you can use these configuration within your tests

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

    <import resource="classpath:/META-INF/spring/applicationContext.xml"/>
    <import resource="classpath:/META-INF/spring/applicationContext-security.xml"/>
</beans>


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/test-configuration.xml")
@Configurable
public class UserDetailsServiceImplIntegrationTest {
...
}
Ralph
A: 

I have a similar setup and it's working fine for me.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/applicationContext-struts.xml", "classpath:/applicationContext.xml" })
@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
@Transactional
public abstract class BaseTests {

My guess is that the problem is in the setup of your project. Do you use Eclipse? Does it load the context files from the /bin folder or from a /src folder? Did you exclude "applicationContext-security.xml" from the build?

Thierry-Dimitri Roy
Well the fact that manual lookup of the UserDetailsService bean works indicates that that context files are in fact being loaded ok. Its just the autowiring part that isn't working for some reason.
David Tinker