Fail to autowire dependency in test - spring

I'd like to write a Spring integration test to make sure that #Autowired correctly puts together my classes, but fail.
The test class
#RunWith(SpringRunner.class)
#DataJpaTest
#EntityScan(basePackageClasses = {ClassUnderTest.class, SomRepository.class, SomeEntity.class})
public class ClassUnderTestIT{
#Autowired private InterfaceUnderTest cut;
#Test public void autowires() {
assertThat(cut).isNotNull();
}
}
Should test that this #Service autowires
#Service
#Transactional
public class ClassUnderTest implements InterfaceUnderTest {
private final SomeRepository repository;
#Autowired
public DefaultWatchlistDataModifier(SomeRepository repository) {
this.repository = repository;
}
}
Paying special attention to the wired dependency
#Repository
#Transactional(readOnly = true)
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public interface SomeRepository extends JpaRepository<SomeEntity, String> {
}
However, all I ever get is the exception
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.[...].InterfaceUnderTest' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
Meanwhile, I have experimented with all kinds of additional annotations, such as #ComponentScan, #EnableJpaRepositories ... whatever I could dig up in the endless number of StackOverflow questions on the topic - all in vain.

Using [#DataJpaTest] will only bootstrap the JPA part of your Spring Boot application. As the unit of your test isn't part of that subset it will not be available in the application context.
Either construct it yourself and inject the dependencies or use a full blown #SpringBootTest instead.

Related

Spring #Autowired behavior different in tests than components

Are the rules/behaviors around #Autowired different when writing tests? It seems that with a test, you can autowire to a concrete type, but if you try the same thing inside a #Component it will fail. This is a contrived example, but it's something I ran into and am just trying to understand better.
Contrived example code:
public interface Gizmo {
void whirr();
}
#Configuration
public class GizmoConfiguration {
#Bean
#Profile("no-dependencies")
public Gizmo fooGizmoBean() {
return new FooGizmo();
}
#Bean
#Profile("!no-dependencies")
public Gizmo barGizmoBean() {
return new BarGizmo();
}
public class FooGizmo implements Gizmo {
#Override
public void whirr() {
}
}
public class BarGizmo implements Gizmo {
#Override
public void whirr() {
}
}
}
Test that runs fine:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {
#Autowired
private GizmoConfiguration.FooGizmo gizmo;
#Test
public void test() {
assertNotNull(gizmo);
}
}
Component that causes java.lang.IllegalStateException: Failed to load ApplicationContext:
#Component
public class TestComponent {
#Autowired
private GizmoConfiguration.FooGizmo gizmo;
}
because of:
No qualifying bean of type 'GizmoConfiguration$FooGizmo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Are the rules/behaviors around #Autowired different when writing
tests?
Not exactly: the rules are actually exactly the same. The difference is in terms of timing with regard to how Spring determines if a given bean is an autowire candidate.
It seems that with a test, you can autowire to a concrete type,
but if you try the same thing inside a #Component it will fail.
I understand why you would think that, since your example demonstrates that behavior, but your analysis is not exactly correct.
So let me explain...
When Spring attempts to perform autowiring for your #Component class, the only information it has about types (i.e., classes and interfaces) for beans coming from #Bean methods is the information available in an #Bean method's formal signature.
In your example, when Spring searches for so-called "autowire candidates" to inject into your #Component, Spring only sees a bean of type Gizmo for your fooGizmoBean() #Bean method. So that's why you see the "No qualifying bean of type 'GizmoConfiguration$FooGizmo'" error, which happens to be completely correct.
If you want Spring to be able to autowire your #Component using the concrete type, you will have to redefine the signature of your fooGizmoBean() #Bean method to return FooGizmo instead of Gizmo.
So, that's the first half of the story. The second half of the story is why the Spring TestContext Framework is able to perform autowiring by the concrete type for the test instance.
The reason that works is that the ApplicationContext has already been completely started (i.e., all beans have been instantiated and all #Bean methods have been invoked by the container) by the time the testing framework attempts to perform dependency injection. By that point in time, the fooGizmoBean() method has already been invoked by Spring, and Spring now knows the concrete type is actually a FooGizmo. Thus, #Autowired FooGizmo gizmo; works in the test.

Test application cannot find autowired bean in same project

I have a jar file that is the persistence layer, ad I just want to test the DAO that are simply autowired into other service layer clasees. But I want to test without any mocking or whatever.
I think this should be pretty simple. I have this in my srs/test/java
#RunWith(SpringRunner.class)
#ComponentScan("com.xxxx")
public class ApplicationTester {
#Autowired
AplicationDocumentDao aplicationDocumentDao;
#Test
private void testAplicationDocumentDao() {
aplicationDocumentDao.allForOrg(1);
}
}
All the DAO's are in the same projust under the usual /src/main/java
When I run the mvn to just run the tests like this:
mvn -Dtest=ApplicationTester test
I get this error:
Error creating bean with name 'xxx.test.ApplicationTester': Unsatisfied dependency expressed through field 'aplicationDocumentDao';
nested exception is 0rg.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xxx.dao.AplicationDocumentDao' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Which means is can't find the Bean of course, but i would think the #ComponentScan would pick up add the Dao's.
How do I get this tester to find all my Dao's (Which are all annotated with #Componenet) and are picked up just fine in the rest for the application.
Any ideas?
** EDIT **
here is the DAO
#Repository
#Component
public class AplicationDocumentDao extends JdbcDaoSupport {
#Autowired
public void setJT(JdbcTemplate jdbcTemplate) {
setJdbcTemplate(jdbcTemplate);
}
public List<ApplicationDocumentBean> allForOrg(int orgId) {
String sql = "SELECT * FROM ApplicationDocument WHERE organizationId = ?";
return (List<ApplicationDocumentBean>) getJdbcTemplate().query(sql, new BeanPropertyRowMapper<ApplicationDocumentBean>(ApplicationDocumentBean.class), orgId);
}
}
Add annotate to your persistence layer class, for example: #Repository or #Component,like this :
#Repository
public interface OrderMapper {}

CDI - #Any and #Inject

Trying to inject bean in a class which has field with #Any annotation. But getting error as -
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.enterprise.inject.Instance' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#javax.inject.Inject(), #javax.enterprise.inject.Any()}
#Named
#Singleton
public class ProcessorFactoryImpl implements ProcessorFactory {
#Inject #Any
private Instance<Processor> processorList;
}
Interface is
public interface Processor {
some method
}
And implementing class is :
#Named
#Singleton
#Default
public class ProcessorImpl implements Processor {
}
For now, I have only one implementation so did not create qualifiers.
You are mixing Spring DI and CDI which are not made to work together.
Choose one of them but not both.
If you choose CDI
#Named
#ApplicationScoped
public class ProcessorFactoryImpl implements ProcessorFactory
{
#Inject
private Processor processor;
}
and
#Dependent
#Default
public class ProcessorImpl implements Processor {
// etc ...
}
In this case, the processor attribute is not a List ! You should think about what you expect. If you want a List<Processor>, you will have to use a CDI Producer instead.
If you choose Spring DI
#Component
public class ProcessorFactoryImpl implements ProcessorFactory
{
#Inject // or #Autowired
private Processor processor;
}
and
#Scope("prototype")
public class ProcessorImpl implements Processor
{
// etc ...
}
Mixing Spring and CDI
As written in the comments below, Spring can use #Inject and #Named as theses annotations are part of JSR-330.
The trouble is that too much framework mixing and using #Inject on an Instance<T>field cannot be achieved like that with Spring as it is a CDI feature.
To use the same feature, use #Provider from Spring.
Example :
CDI
#Inject
private Instance<MyClass> lazyInstance;
usage :
MyClasse instance = lazyInstance.get();
Spring
#Inject // or #Autowired
private Provider<MyClass> lazyInstance;
usage (same as CDI):
MyClasse instance = lazyInstance.get();
Have you tried List instead of Instance?
#Autowired
private List<Processor> processorList;

Spring Test + Mockito.mock - Spring fails because it tries to load the mocked bean #Autowired dependencies

I can't find out why the following simple scenario is failing: I have a Spring application with a filter that loads a Spring bean from the application context:
public class MyFilter implements Filter{
private IPermissionService permissionService;
public void init(FilterConfig filterConfig) throws ServletException {
WebApplicationContext ac = null;
try{
ac = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
permissionService = ac.getBean(PermissionServiceImpl.class);
PermissionServiceImpl has an #Autowired attribute dataSource so in my TestNG test, I mock it in the Spring applicationContext:
#Configuration
public class MyFilterSpringTestConfig{
#Bean
public IPermissionService permissionService(){
return Mockito.mock(PermissionServiceImpl.class);
}
MyTest:
#Test
#WebAppConfiguration
#ContextConfiguration(classes=MyFilterSpringTestConfig.class)
public class MyFilterSpringTest extends BaseSpringFilterTest{
...
The problem is that on Spring initialization I get an exception complaining that PermissionServiceImpl's dataSource dependency is not satisfied. Since I wrapped it with a mock, why is it still failing? How could I fix it?
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] 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), #org.springframework.beans.factory.annotation.Qualifier(value=myDataSource)}
When mocking a class using Mockito (or any other mocking framework) that class is still an instance of the original class. With that comes that it also contains all the annotations and class information with it.
So when you create a mock of the class it still detects all annotations on it and tries to full fill that. I.e. #Autowire other instances.
Either don't use auto wiring or don't mock the class but the interface (which doesn't contain that information).

Spring Data - MongoDB - JUnit test

I would have a question concerning Spring Data - MongoDB and JUnit test.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { UserRepository.class, User.class })
public class MyJUnitTest {
The UserRepository looks like this:
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(final String username);
}
I get the following Exception:
Failed to instantiate [... .repository.UserRepository]: Specified class is an interface
My question now would be, how to do it, that UserRepository is instantiate although there is no implementation class because Spring Data does the implementation by its own? If I do not mark USerRepository with #Repository than Spring does not create a bean object
[EDIT]
I have tried the example of the link you posted and it works fine if I run the application over the main- method.
Then I tried to implement a test class but in this case I get the same exception:
Error creating bean with name 'hello.test.TestClass': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private hello.CustomerRepository hello.test.TestClass.repository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.CustomerRepository] 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)}
My test class looks like this in src/test/java/hello/test (hello.test is the package):
#ComponentScan("hello")
#EnableMongoRepositories(basePackages = "hello")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { CustomerRepository.class, Customer.class })
public class TestClass {
#Autowired
private CustomerRepository repository;
#Test
public void testMethod() {
System.out.println("repositoryd: " + repository);
}
}
and my CustomerRepository looks like this (with #Configuration annotation):
#Configuration
public interface CustomerRepository extends MongoRepository<Customer, String> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}
Actually I don't know which annotations I need in order to get the test running - Maybe you would have another suggestion in order that I can solve this issue.
For Spring Boot 1.5.8.RELEASE
You can use #SpringBootTest to bootstrap all you spring configurations.
Your test will look like
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeRepositoryTests {
#Autowired
private SomeRepository someRepository;
#Test
public void someTest() {
someRepository.someMethod(...);
// assertions
}
}
Of course you want to use embedded mongodb for test so add
for Maven
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
for Gradle
testCompile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
Your repo CustomerRepository doesn't require #Configuration or #Repository annotations. Spring will do it for you as you extends base Repository classes.
To setup Mongo Repositories you need to extend ApplicationContext with the following annotations.
#Configuration
#EnableAutoConfiguration // create MongoTemplate and MongoOperations
#EnableMongoRepositories(basePackages = "hello.test") // Create your repos
public class ApplicationConfig {
}
You also would like to use in-memory database for you unit/integration tests so them won't alter productions database.
To enable it just add the following dependency:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.2</version>
<scope>runtime</scope>
</dependency>
Finally, configure you test class with the ApplicationContext
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ApplicationConfig.class)
public class MyJUnitTest {
// Test cases ...
}
Within #SpringApplicationConfiguration you need to point to a configuration class. Neither UserRepository nor User probably are one I assume.
To get started with both Spring and Spring Data MongoDB fundamentals, be sure to check out this getting started guide.
If using junit 5 and you want to start up spring in the most lightweight way possible you will need the following annotations on your IntegrationTest class
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyMongoService.class})
#EnableMongoRepositories(basePackageClasses = MyRepository.class)
public class MyMongoIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private MyRepository myRepository;
#Autowired
private MyMongoService myMongoService; <-- Assuming this injects MyRepository
}

Resources