4

I created an interface and a class:

public interface UserService {
    List<User> listAll();
}

@Transactional
public class DefaultUserService implements UserService {
    private String tableName;
    public List<User> listAll() { someDao.listAllFromTable(tableName); }
    public void setTableName(String tableName) { this.tableName = tableName; }
}

Also in my application context xml file context.xml, I defined:

<bean id="userService" class="mypackage.DefaultUserService">
    <property name="tableName" value="myusers" />
</bean>

Then I want to test the DefaultUserService:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:context-test.xml"})
@TransactionConfiguration(transactionManager = "testTransactionManager")
@Transactional
public class UserServiceTest {

    @Autowired
    private DefaultUserService userService;

    @Before
    public void setup() {
        userService.setTableName("mytesttable");
    }
    @Test
    public void test() {
        // test with userService;
        userService.listAll();
    }
}

Notice it uses context-test.xml, which imported the original context.xml:

<import resource="classpath:context.xml"/>

Unfortunately, when the test starts, spring throws exception:

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'mypackage.UserServiceTest': 
Injection of autowired dependencies failed; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Could not autowire field: 
private mypackage.DefaultUserService mypackage.userService

nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [mypackage.DefaultUserService] 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)}

I'm not sure where is wrong, why spring can't find the bean DefaultUserService I defined?

Freewind
  • 193,756
  • 157
  • 432
  • 708
  • The `` declaration you have is in `context-test.xml`? – Sotirios Delimanolis Nov 29 '13 at 03:24
  • Did you mean to type `public class DefaultUserService` in that first codeblock? – Dan Nov 29 '13 at 03:36
  • @Sotirios, see my updated question, I have that declaration. – Freewind Nov 29 '13 at 03:43
  • @Dan, yes. Spring can't inject `DefaultUserService` bean for my test. – Freewind Nov 29 '13 at 03:44
  • Are you building your app with Maven? Do you have a possibly empty file called `context.xml` in `/src/test/resources`? – Sotirios Delimanolis Nov 29 '13 at 03:48
  • You have `` in your context.xml? And just curious but shouldn't there be a setter for `userService` in `UserServiceTest`? – Dan Nov 29 '13 at 04:05
  • @Dan If OP didn't, there wouldn't be `Could not autowire field:`. Injection with `@Autowired` doesn't require setters. – Sotirios Delimanolis Nov 29 '13 at 04:07
  • 2
    It's because `@Transactional` places the bean is behind jdk proxy http://stackoverflow.com/a/18875681/241986. You can try setting the table name with a property placeholder `@Value("${someprop}")`. Or create another interface that will have `setTableName` and the test will be referring to.. I'm not sure there are any neat solutions of the problem. – Boris Treukhov Nov 29 '13 at 07:13
  • @BorisTreukhov, you are right!! Many thanks to you. Could you make your comment as answer, and I will accept it! – Freewind Nov 29 '13 at 07:36

3 Answers3

2

Try to replace the class DefaultUserService to the interface UserService

public class UserServiceTest {

    @Autowired
    private UserService userService;
    ....

}
Sergey Morozov
  • 4,528
  • 3
  • 25
  • 39
  • It works, but I can't call 'setTableName(...)' to set my test table name, since `UserService` doesn't have such a method declaration. – Freewind Nov 29 '13 at 06:41
2

It's because @Transactional places the bean is behind a jdk proxy implementing UserService interface, after that the bean is only available as UserService and not DefaultUserService. See https://stackoverflow.com/a/18875681/241986.

You can try setting the table name with a property placeholder @Value("${someprop}") and define that property in test context, or create another interface that will expose setTableName(), and autowire that helper interface into the test case.

I'm not sure there are any easy solutions of the problem, I think this task can be subsumed under the problem of bean redefinition in Spring test-context framework Spring beans redefinition in unit test environment

Community
  • 1
  • 1
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
1

You have not defined the getter for your property tableName in your implementing class.Spring IOC container works on the POJO model

Deepak
  • 2,287
  • 1
  • 23
  • 30