I've recently upgraded a project from Spring 4.3.1 to 4.3.4 and what used to work fine, now just won't work for me.
I use JPA, which holds a series of repository classes:
/**
* Spring Data JPA repository for the DrugQualityCategory entity.
*/
public interface DrugQualityCategoryRepository extends JpaRepository<DrugQualityCategory,Long> {
#Query(value = "Select a from DrugQualityCategory a where a.oldId = ?1")
DrugQualityCategory findOneByOldId(Integer oldId);
}
We also use ElasticSearch for the search engine, which creates a series of SearchRepositories like this:
/**
* Spring Data ElasticSearch repository for the Publication entity.
*/
public interface DrugQualityCategorySearchRepository extends ElasticsearchRepository<Publication, Long> {
}
One of the issues with ES is that it needs to regularly update its indices, so for this we built a test where it injects all repositories and rebuilds them from the JPA repository, looking like this when we inject the repos:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#Transactional
#ActiveProfiles("syncElasticsearch")
public class SyncMysqlElasticSearch {
private Logger logger = LoggerFactory.getLogger(getClass());
#Inject DrugQualityCategoryRepository drugQualityCategoryRepository;
#Inject TechniqueRepository techniqueRepository;
#Inject TradeDrugRepository tradeDrugRepository;
#Inject SurveyDataRepository surveyDataRepository;
#Inject RQAAQualityRepository rqaaQualityRepository;
And then we fill the ES instances:
drugQualityCategorySearchRepository.save(drugQualityCategoryRepository.findAll());
formulationSearchRepository.save(formulationRepository.findAll());
innDrugSearchRepository.save(innDrugRepository.findAll());
locationSearchRepository.save(locationRepository.findAll());
manufacturerSearchRepository.save(manufacturerRepository.findAll());
Now, the issue I have is that when I try to start the test, I keep on getting:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name [class name] Unsatisfied dependency expressed
through field [field name] nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [class name] available: expected at least
1 bean which qualifies as autowire candidate. Dependency annotations: {#javax.inject.Inject()}
I've checked several questions here like this and this, had a quick browse on the Spring docs, but couldn't find anything relevant
I've obviously tried all the #Autowired, #Component and #Repository tags in different places, to no positive result
You need to annotate your test class with a #ContextConfiguration annotation which points to the relevant #Configuration class which scans and registers all these beans you're trying to inject.
See the java docs for #WebAppConfiguration:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/context/web/WebAppConfiguration.html
Note that #WebAppConfiguration must be used in conjunction with #ContextConfiguration, either within a single test class or within a test class hierarchy.
Related
I'm working on two relative projects. One project is used to connect to postgresql and another project import the connector project as dependency to connect to postgresql. In connector project I used EntityManager to access to database.
In Repository class I use entity manager to get entity
#Transactional
#Repository
public class ConfigDetailRepository {
private EntityManager entityManager;
#Autowired
public ConfigDetailRepository(final EntityManager entityManager) {
this.entityManager = entityManager;
}
public void doSomething()
And in Service class I autowire repository
#Configurable
#Service
public class SampleService {
#Autowired
private ConfigDetailRepository configDetailRepository;
public class (){ configDetailRepository.doSomething()}
This connector project is workable and able to get data from database. In my second project I import the connector project and add this dependency in pom.xml
<dependency>
<groupId>package name</groupId>
<artifactId>package-id</artifactId>
<version>package-version</version>
</dependency>
And that's my Application.class and class import SampleService
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
#ComponentScan(basePackages = {"package name"})
public class CassandraTestApplication {
public static void main(String[] args) {
SpringApplication.run(CassandraTestApplication.class, args);
}
#Configurable
#Component
public class TestConnector {
#Autowired
SampleService sampleService;
public void doSomtehing() {
sampleService.doSomething();
}
}
When I run the project I got following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in package.repository.ConfigDetailRepository required a bean of type 'javax.persistence.EntityManager' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testConnector': Unsatisfied dependency expressed through field 'sampleService'; nested exception is org.springframework.beans
.factory.UnsatisfiedDependencyException: Error creating bean with name 'sampleService': Unsatisfied dependency expressed through field 'configDetailRepository'; nested exception is org.springframework.beans.factory.UnsatisfiedDepende
ncyException: Error creating bean with name 'configDetailRepository' defined in URL [jar:file:/C:/Users/.m2/repository/com/connector/cassandra-db/0.0.1-SNAPSHOT/cassandra-db-0.0.1-SNAPSHOT.jar!/com/repository/ConfigDetailRepository.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying b
ean of type 'javax.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
I check the connector project and it can work. Then I tried exclude=HibernateJpaAutoConfiguration.class but that didn't work. Can anyone tell me why the entity manager cannot be created?
The problem is the exclude = {DataSourceAutoConfiguration.class}. With this you are excluding the configuration of the DataSource by Spring Boot. Unless you specify one yourself using an #Bean method this basically prevents you from doing all DB related work.
JPA requires access to your database and without a configured DataSource this will not be possible. Due to the missing DataSource Spring Boot will also not configure JPA. No JPA will lead to this error.
To fix, remove the exclude and provide the configuration for the database.
I am using spring-boot (2.2.7.RELEASE) with webflux for a small-ish rest service with mongodb. I have 2 repositories (ARepository, BRepository) implemented something like this:
#Repository
public interface ARepository extends ReactiveMongoRepository<DataDTO, Integer> {
}
I also have an extra service which is using these 2 and a ReactiveMongoTemplate instance. It's wired something like this:
#Slf4j
#Service
public class DefaultTheService implements TheService {
private final ARepository aRepository;
private final BRepository bRepository;
private final ReactiveMongoTemplate mongoTemplate;
#Autowired
public DefaultTheService(ARepository aRepository, BRepository bRepository, ReactiveMongoTemplate mongoTemplate) {
this.aRepository = aRepository;
this.bRepository = bRepository;
this.mongoTemplate = mongoTemplate;
}
}
All is good, it works as it should, no problems there.
Now, I want to write some integration tests and I started like this:
#DataMongoTest
#Slf4j
class DefaultTheServiceTest {
#Autowired
private ARepository aRepository;
#Autowired
private BRepository bRepository;
#Autowired
private ReactiveMongoTemplate reactiveMongoTemplate;
#Autowired
private DefaultTheService defaultTheService;
#Test
void runTheMagicTest() {
// empty body, I just want to see if everything wires up correctly.
}
}
When I want to execute runTheMagicTest (junit5), I am always getting this error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.DefaultTheServiceTest': Unsatisfied dependency expressed through field 'defaultTheService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.DefaultTheService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:130)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.DefaultTheService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1716)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1272)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
*! notice the bean name: DefaultTheServiceTest
Normally, I could maybe get away simply creating an instance of DefaultTheService before each test and then calling the methods I want to test, but I'd like to give it a try using spring.
If I simply remove the private DefaultTheService defaultTheService declaration - the test is "running". I am pretty sure I am missing something stupid and I am chasing my tail.
So, can someone ease my pain and point me to the (possibly?) obvious error I am making?
Thanks!
#DataMongoTest:
Annotation that can be used for a MongoDB test that focuses only on MongoDB components.
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MongoDB tests.
Try a #SpringBootTest for a "full"/default application context instead.
For general information refer to:
https://docs.spring.io/spring-framework/docs/current/reference/html/testing.html#testing
For (auto-)configuration details & refinement to:
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing
... setting #DataMongoTest(useDefaultFilters = false) (+ fine tuning include-/excludeFilters) can also do the desired.
I have following test:
#SpringBootTest(classes = {SomeService.class, DtoMapperImpl.class})
class SomeServiceTest {
And following mapper:
#Mapper(componentModel = "spring")
public interface DtoMapper {
EntityDto toDto(Entity entity);
}
I'm not changing packages (this means DtoMapperImpl is in the same package as DtoMapper)
As soon as I change Impl to interface my test fails:
#SpringBootTest(classes = {SomeService.class, DtoMapper.class})
class SomeServiceTest {
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'someService': Unsatisfied dependency
expressed through constructor parameter 2; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'DtoMapper' available: expected at least 1
bean which qualifies as autowire candidate. Dependency annotations: {}
Can you please advise best way solving this? I'm on MapStruct 1.3.1.Final
The problem actually has nothing to do with MapStruct, but rather to how SpringBootTest#classes is used.
The classes in SpringBootTest is meant to provide your components that should be used to load in the test.
From the JavaDoc:
The component classes to use for loading an ApplicationContext. Can also be specified using #ContextConfiguration(classes=...). If no explicit classes are defined the test will look for nested #Configuration classes, before falling back to a #SpringBootConfiguration search.
Returns:
the component classes used to load the application context
In your case you have 2 classes:
SomeService - which I assume is a class annotated with #Service and Spring will correctly load it
DtoMapper - this is the MapStruct mapper which is an interface and it isn't a component. The component which you want for your tests is DtoMapperImpl
You have several options to fix this:
Use the Impl class
You can use the DtoMapperImpl (the Spring Component class) in your SpringBootTest#classes, your test will then load the correct component
Use a custom configuration class that will component scan your mappers
#TestConfiguration
#ComponentScan("com.example.mapper")
public class MappersConfig {
}
And then use this in your SpringBootTest#classes. E.g.
#SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
...
}
Create following configuration (should point where mappers are):
#TestConfiguration
#ComponentScan("some.package.mapper")
public class MappersConfig {
}
And modify slice:
#SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
I would suggest a little improvement for accepted answer so that you don't have to write package name as hard-coded string, but use ComponentScan basePackageClasses instead:
#TestConfiguration
// #ComponentScan("some.package.mapper")
#ComponentScan(basePackageClasses = DtoMapper.class)
public class MappersConfig {
}
But there is an obvious drawback in this (as well as accepted answer) approach: the whole package will be scanned which may contain undesired classes.
I have encountered this weird behavior of Spring in Spring boot 1.4.0. Spring basically tells me that it cannot autowire a bean to the resource, because it did not found itself for dependency.
UnsatisfiedDependencyException: Error creating bean with name 'restResource': Unsatisfied dependency expressed through field
'fooService': No qualifying bean of type [**aaa.FooService**] found for dependency [**aaa.FooService**]
FooService is autowired in the resource. When I #Autowire it into #Configuration file, which creates the resource, it is injected there as expected.
This works:
public class ServiceMocksRestConfig extends WebMvcConfigurerAdapter {
#Autowired
private FooService fooService; //instance here
#Bean
public FooResource fooResource() {
return new FooResource(); // debuger stop here
}
//Debugger step into
#RestController
public class FooResource {
#Autowired
private FooService fooService; //bang
Does someone has any idea, what might went wrong?
Funny stuff is that when I run the app from tests using boot spring runner, it also works (everything, including this resource)
I managed today to find the root cause. Its Spring Boot devtools - more precisely its split classloader (related bug: https://github.com/spring-projects/spring-boot/issues/3316)
When I put a breakpoint in ListableBeanFactory when the child REST #Configuration was about to #Autowire the FooService
and did FooService instanceof FooServiceInterface, it returned false.
And when I did FooService.class.getClassLoader() and beanfactory.getBean("fooService" /cannot use class here, would trigger not found exception/).getClass().getClassloader() these were different (one was AppClassLoader and the other was devtools restartable class loader).
Solution: remove boot devtools from classpath.
The exact reason for this is that, there is no bean initialized for type FooService inside the Spring IOC container at the moment the run-time tries to autowire FooService into ServiceMocksRestConfig .
It can be cause by different mistakes in development. This article addresses each and every possible mistakes that can cause this problem.
I am using Spring MVC and Spring Integration in my application.
VendorService is my Interface whose implementation is taken care of by Spring Integration.
I am injecting VendorService's instance into my Controller by Autowiring it.
Here is my Controller..
#Autowired(required=true)
#Qualifier("vendorService")
VendorService vendorService;
I am getting the following error,
No qualifying bean of type [com.sample.service.VendorService] 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=vendorService)}
I think you are not applying #Service or #Repository Annotations in your implementation class of interface (VenderService).
try this :-
#Repository
pulblic class VenderServiceImpl implements VenderService{
// Do your job here.
}
I hope it will work.
Spring can't find class which implements VenderService interface. Help him by adding #Service or #Component annotation to your class which implements VenderService.
The error indicates that Spring could not find an implementation of VendorService to satisfy your autowiring request. It can only use classes that implement the specified interface to do this, and they must either be defined in a context file, or found by package scanning (through the use of an annotation).
Here is good article on how spring loads beans in application context. Either you can manually define that as a bean using
1) <bean id="" class=""> in application context.
or if you want to annotate your calss then you have to add auto scanning of component using
2)<context:component-scan base-package="XX.XX" />.
This error occurs when spring can't find bean id in application context.
http://www.mkyong.com/spring/spring-auto-scanning-components/