Wiring beans during integration tests with embedded mongo - java

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.

Related

Spring AutoWire not working when invoking CRUD Repository

I am having a spring boot app which connects to database using Spring JPA and retreives the data for processing. When I try to autowire using I am getting below error. Can someone please throw somelight.
I have added application entry point , Repo and Tasklet below. Please check it.
Application Entry Point
package com.printbatch;
#SpringBootApplication
#EnableJpaRepositories("com.printbatch.config")
public class AgencyBillPayFileUploadApplication implements
CommandLineRunner{
public static void main(String[] args) {
SpringApplication.run(AgencyBillPayFileUploadApplication.class, args);
}
//access command line arguments
#Override
public void run(String... args) throws Exception {
System.out.println("args");
System.out.println(args[0]);
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"ABPBatchInfrastructure.xml",
"AgencyBillPayFileUploadApp.xml"
);
JobLauncher jobLauncher = ctx.getBean(JobLauncher.class);
Job job = ctx.getBean(Job.class);
/*
* jobLauncher.run(job, new JobParametersBuilder().addString("inputResource",
* "file:./products.zip") .addString("targetDirectory",
* "./importproductsbatch/").addString("targetFile", "products.txt")
* .addString("date", "2020-06-02").toJobParameters());
*/
jobLauncher.run(job,
new JobParametersBuilder().addString("inputResource", "file:./products.zip")
.addString("targetDirectory", "./importproductsbatch/").addString("targetFile", "products.txt")
.addString("date", "2005-07-29").toJobParameters());
}
}
package com.printbatch.config;
#Component
public interface PrintJobItemRepo extends CrudRepository<PrintJobItem, Integer> {
List<PrintJobItem> findByPrintStatusEquals(String printStatus );
}
Calling this component in this class
package com.printbatch.tasklet;
public class ReadInputFile implements Tasklet {
private Resource inputResource;
private String targetDirectory;
#Autowired
private PrintJobItemRepo pjr;
}
Error below
Error creating bean with name 'scopedTarget.decompressTasklet': Unsatisfied dependency expressed
through field 'pjr'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.printbatch.repo.PrintJobItemRepo' available: expected at least 1 bean which qualifies as
autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.printbatch.repo.PrintJobItemRepo' available: expected at least 1 bean which qualifies as
autowire candidate. Dependency annotations:
Stack trace tells pretty well what is going on. com.printbatch.repo.PrintJobItemRepo
You are trying to autowire a class that is not in the JPA repository path. Move your class into com.printbatch.config package.
Remove #Component from PrintJobItemRepo. Is ReadInputFile a managed bean? If not, you should annotate it with Component, Service, or make it as a Bean, so it must become managed object. Spring can only inject managed bean into another managed bean.
Add #Component to ReadInputFile class.
Remove #Component from PrintJobItemRepo class.
We generally use #Repository annotation over such classes.
Check out some examples to see how this works -
Spring boot crud example
Another Spring boot crud example

Springboot application throws a NoSuchBeanDefinitionException trying to Autowire CouchbaseCluster [duplicate]

This question already has an answer here:
What is a NoSuchBeanDefinitionException and how do I fix it?
(1 answer)
Closed 2 years ago.
I work with a legacy springboot application which has a huge com.mycompany.MyApplication class where there are injected a bunch of spring objects through #Autowire annotation.
Trying to split MyApplication class up, I created several classes and put them in different packages but I got an issue with CouchbaseCluster autowire injection; the error is:
AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myApplication': Unsatisfied dependency expressed through field 'step'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jobSteps': Unsatisfied dependency expressed through field 'couchbaseFlushCacheTasklet'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'couchbaseFlushCacheTasklet': Unsatisfied dependency expressed through field 'cluster'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.couchbase.client.java.CouchbaseCluster' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
***************************
APPLICATION FAILED TO START
***************************
Description:
Field cluster in com.mycompany.batch.tasklet.CouchbaseFlushCacheTasklet required a bean of type 'com.couchbase.client.java.CouchbaseCluster' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.couchbase.client.java.CouchbaseCluster' in your configuration.
Here is an example of my code:
package com.mycompany;
#EnableBatchProcessing
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
#Autowired
JobSteps step;
...
}
package com.mycompany.batch;
#Configuration
public class JobSteps {
#Autowired
CouchbaseFlushCacheTasklet couchbaseFlushCacheTasklet;
#Autowired
CouchbaseDeleteNonBatchTasklet couchbaseDeleteNonBatchTasklet;
...
}
If you note in the next code snipped, cluster uses the #SuppressWarnings annotation because Intellij marks this with this error: 'Could not autowire. No beans of 'CouchbaseCluster' type found.' however it was before and after my changes, but before the application ran without issues.
package com.mycompany.batch.tasklet;
#Component
public class CouchbaseFlushCacheTasklet {
#SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
#Autowired
CouchbaseCluster cluster;
...
}
I am putting this next class to show that there is any problem with the bean Budget, which is located in the same library/package than CouchbaseCluster com.couchbase.client.java.Bucket
package com.mycompany.batch.tasklet;
#Component
public class CouchbaseDeleteNonBatchTasklet {
#Autowired
Bucket bucket;
...
}
POM dependencies associated with couchbase
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-couchbase</artifactId>
<version>3.1.9.RELEASE</version>
</dependency>
I tried to specify the scanBasePackeges but didn't work.
#SpringBootApplication(scanBasePackages = {"com.mycompany", "com.couchbase.client"})
Any clue will be appreciated since I have spent a lot of time looking for a solution and so far none of the suggested things in similar posts have worked.
I solved it :-)
Instead of autowire this
#Autowired
CouchbaseCluster cluster;
I autowire this
#Autowired
Cluster cluster;
I am not sure how was possible that at some point it worked autowiring CouchbaseCluster
First thing CouchbaseCluster class should be part of the bean life cycle, it should be annotated with an annotation like #Component etc. if so you must check you should not be creating the object of any class using the new keyword.
So in your case, it will be resolved something like this. add the below method into JobSteps.
#Bean
public CouchbaseCluster couchbaseCluster(){
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.builder()
.bootstrapCarrierDirectPort(couchbase.getMappedPort(11210))
.bootstrapCarrierSslPort(couchbase.getMappedPort(11207))
.bootstrapHttpDirectPort(couchbase.getMappedPort(8091))
.bootstrapHttpSslPort(couchbase.getMappedPort(18091))
.build();
CouchbaseCluster cc = CouchbaseCluster.create(env);
return cc;
}
Modified the CouchbaseEnvironment object according to your configuration.
Have you tried creating your Cluster in the #PostConstruct?
#Service
public class CouchbaseFlushCacheTasklet {
private Cluster cluster;
#PostConstruct
private void init() {
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.create();
cluster = CouchbaseCluster.create(env, "localhost");
}
...
}
Code from: https://www.baeldung.com/couchbase-sdk-spring
Alternatively, try adding an #Bean producer somewhere instead of using a #PostConstruct.

Spring - No default constructor found

I have a class which looks like this:
#Service("myService")
public class MyServiceImpl {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
and I also have a test class which looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
MyServiceImpl.class})
...
I get this exception:
Injection of autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not autowire
field: private org.springframework.messaging.simp.SimpMessagingTemplate
myPackage.MyServiceImpl.simpMessagingTemplate; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.messaging.simp.SimpMessagingTemplate] 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)}
Does anyone know what I can do in order to get it work, SimpMessagingTemplate does not have a default constructor.
SimpMessagingTemplate seems to have either no default constructor or is not annotated with #Component (or #Service or another sub-class of #Component); or both.
Please check that a default constructor is available and the class is configured to be a Spring bean.
Its not related to missing constructor, but Spring fails to find proper bean to inject into your test class,
Two options to solve it as I see
#ContextConfiguration(classes = {
MyServiceImpl.class, SimpMessagingTemplate.class})
Add #mock SimpMessagingTemplate simpMessagingTemplate; to your test class

Spring #Inject not working

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.

How to create a mock spring bean for a class having autowired dependencies

Suppose I have a class called MainClass.
public class MainClass {
#Autowired
AutoWiredClass autoWiredClass;
}
I am trying to create a mock bean of MainClass using Mockito.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
public class AutowiringTest {
#Configuration
static class AutowiringTestConfiguration{
#Bean
public MainClass mainClass() {
return Mockito.mock(MainClass.class);
}
}
#Autowired
MainClass mainClass;
#Test
public void testBeanCreation(){
assertNotNull(mainClass);
}
}
I am getting this error while running the test case.
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: autowiring.AutoWiredClass autowiring.MainClass.autoWiredClass; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [autowiring.AutoWiredClass] 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 know I can achieve this using #Mock and #InjectMocks. But that's not the solution I want.
My requirement is to create a mock bean of MainClass without creating an actual bean of AutowiredClass. Please help me how to achieve this.
As Florian has already commented, you should try to create tests that do not need Spring at all, and you will not have those problems.
But, if there is no workaround possible, you can use a bit of magic with the AutoMockRegistryPostProcessor.
You just need to add the AutoMockRegistryPostProcessor to the #ContextConfiguration, and it will create mocks for your missing dependencies:
#ContextConfiguration(classes = { AutowiringTest.class, AutoMockRegistryPostProcessor.class })
public class AutowiringTest {
// no complains anymore, a mockito mock will be created for AutoWiredClass
The AutoMockRegistryPostProcessor class is not in maven, you will need to copy it in your project.
The docu is here.

Categories

Resources