-1

I'm developing a spring application using JavaFX (I'm NOT using Spring MVC) and I have standard separation of concerns with controllers - services - DAOs. I'm using JdbcTemplate. I'm willing to write down jUnit tests for one of my services (I have already did it for some of them). The specific thing is that the service is autowiring two DAOs(one of which uses transactions itself) and furthermore it has one method, which is @Transactional. This is how my test exactly looks:

package org.impactvolunteers.management.service.impl;

imports ...;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:testContext.xml" })
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class RegisterVolunteerServiceImplTest extends
        AbstractRegisterVolunteerServiceTest {
    @Autowired
    private RegisterVolunteerService registerVolunteerService;

    @Before
    public void setUp() {
        setRegisterVolunteerService(registerVolunteerService);
    }

}

And my Service implementation:

package org.impactvolunteers.management.service.impl;

imports ...;

public class RegisterVolunteerServiceImpl implements RegisterVolunteerService {
    @Autowired
    private VolunteerDao volunteerDao;

    @Autowired
    private UserDao userDao;

    @Transactional(rollbackFor = { ServiceException.class,
            ValidationException.class })
    public Volunteer registerVolunteer(User user, Volunteer volunteer)
            throws ServiceException, ValidationException {
        UserValidator.validateData(user);
        VolunteerValidator.validateData(volunteer);
        try {
            User ret = userDao.create(user);
            volunteer.setUser(ret);
            return volunteerDao.create(volunteer);
        } catch (PersistenceException e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }
}

And in application-Context:

<context:component-scan base-package="org.impactvolunteers.management"/>

    <!-- enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

    <!-- Transaction Manager -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    ...
    <bean id="registerVolunteerService" class="org.impactvolunteers.management.service.impl.RegisterVolunteerServiceImpl" >
    </bean>

Here is the error message:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.impactvolunteers.management.service.impl.RegisterVolunteerServiceImplTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.impactvolunteers.management.service.RegisterVolunteerService org.impactvolunteers.management.service.impl.RegisterVolunteerServiceImplTest.registerVolunteerService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.impactvolunteers.management.service.RegisterVolunteerService] 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:288) ............ Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.impactvolunteers.management.service.RegisterVolunteerService] 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.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:988) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:858) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486) ... 28 more

And my test-Context.xml:

    <context:annotation-config/>

    <context:component-scan base-package="org.impactvolunteers.management"/>

    <bean id="jdbc" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- Spring JDBC Utility for in-memory database -->
    <jdbc:embedded-database id="dataSource" type="HSQL"/>

Two strange things I noticed:

  1. I have no problem starting the application as Java application and the service works as intended. I have only problems testing the service as jUnit test.
  2. A service with the exact same context (and the same context for the test as the test showed here) and bean definition is being successfully tested with the only difference that it does not contain @Transactional annotations, where some of the DAOs under it do (VolunteerDaoImpl which implements VolunteerDao does contain a @Transactional annotation).
Ivaylo Toskov
  • 3,911
  • 3
  • 32
  • 48

2 Answers2

2

The class RegisterVolunteerServiceImpl must be annotated as service.

If the class is not annotated as a service it will not befound by the component-scan. So the bean with the name is not instanciated and can not be autowired.

In your main application-context you add the bean without component-scan

 <bean id="registerVolunteerService" class="org.impactvolunteers.management.service.impl.RegisterVolunteerServiceImpl" >
    </bean>
M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Jens
  • 67,715
  • 15
  • 98
  • 113
  • So can you explain how is the Service being found when starting the application as a normal Java application? And why are my other services being found in their test cases when they are not annotated as a service? – Ivaylo Toskov Jun 10 '14 at 12:59
  • You shouldn't have a complete test context, you should only override the beans that need to be different or added. Next load both your normal context file and the modified test-context (which should contain probably only a `DataSource` and maybe your `JdbcTemplate`. – M. Deinum Jun 10 '14 at 14:00
1

That testContext.xml doesn't import your general-purpose application context, and it also doesn't define that registerVolunteerService bean.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • It was not the problem, as adding the Service annotation fixed it and the other test cases worked as intended. – Ivaylo Toskov Jun 10 '14 at 12:55
  • That was *exactly* the problem, as you simply got Spring to create the bean in a different way (component-scanning, this time). Consequently, you can also remove the bean declaration from your regular XML context. – chrylis -cautiouslyoptimistic- Jun 10 '14 at 13:01
  • 1
    I think I understand now. The reason it is being found while starting it as a normal application is that it is defined in the applicationContext as a bean, but when I start it as a jUnit test, it has to be scanned for, but without a @Service annotation it's not being found, correct? If this is right, the question left for me is why are my other not annotated services being found while testing, but this particular one not? As I said the only general difference I notice is that this service has a transactional method. – Ivaylo Toskov Jun 10 '14 at 13:10
  • @IvayloToskov Spring can find your bean either through scanning, which needs `@Component` or one of its subannotations, like `@Service`, or through specifically configuring it. As to why some components do get loaded in your test context, it's impossible to tell without the entire configuration. – chrylis -cautiouslyoptimistic- Jun 10 '14 at 13:30