I'm trying to create unit/integration test using Junit5 for specific service classes to avoid overload the whole project.
So here I try to run the EmailService with its dependency classes inside, but I got java.lang.IllegalStateException: Failed to load ApplicationContext. Error creating bean with name 'emailSenderService. No qualifying bean of type 'org.springframework.mail.javamail.JavaMailSender' available: expected at least 1 bean which qualifies as autowire candidate.'.
Do I must have to run the whole application to test a single service?
build.yml
{
testImplementation ('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'junit', module: 'junit'
}
testImplementation "org.junit.jupiter:junit-jupiter:5.4.1"
}
Service:
public class EmailSenderService {
private final JavaMailSender sender;
private final SpringTemplateEngine templateEngine;
private final MessageSource i18n;
public EmailSenderService(JavaMailSender sender, SpringTemplateEngine templateEngine,
#Qualifier("messageSource") MessageSource i18n) {
this.sender = sender;
this.templateEngine = templateEngine;
this.i18n = i18n;
}
}
test class:
#SpringBootTest(
classes = {EmailSenderService.class}
)
#ExtendWith({SpringExtension.class})
class EmailServiceTest {
private static GreenMail smtp;
#Autowired
private EmailSenderService mailService;
#BeforeAll
static void init() {
smtp = new GreenMail(new ServerSetup(3026,null,"smtp"));
smtp.start();
}
#AfterAll
static void tearDown() {
smtp.stop();
}
#BeforeEach
void clearUp() throws FolderException {
smtp.purgeEmailFromAllMailboxes();
}
#Test
void testNewBidRequestEmail() throws MessagingException {
EmailMessageTemplateDto contact = new EmailMessageTemplateDto("test","test#test.com","test message");
mailService.sendUserContactEmail(contact);
Assertions.assertTrue(smtp.waitForIncomingEmail(1));
}
}
Error:
2019-04-03 14:56:06.146 WARN 732 --- [ main]
o.s.w.c.s.GenericWebApplicationContext : Exception encountered
during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'emailSenderService': Unsatisfied
dependency expressed through constructor parameter 0; nested exception
is org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.mail.javamail.JavaMailSender' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations: {} 2019-04-03 14:56:06.153 ERROR 732 --- [
main] o.s.b.d.LoggingFailureAnalysisReporter :
*************************** APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in
com.server.server.service.EmailSenderService required a bean of
type 'org.springframework.mail.javamail.JavaMailSender' that could not
be found.
Action:
Consider defining a bean of type
'org.springframework.mail.javamail.JavaMailSender' in your
configuration.
2019-04-03 14:56:06.159 ERROR 732 --- [ main]
o.s.test.context.TestContextManager : Caught exception while
allowing TestExecutionListener
[org.springframework.test.context.web.ServletTestExecutionListener#342c38f8]
to prepare test instance
[com.server.server.test.junit.EmailServiceTest#4c7a078]
java.lang.IllegalStateException: Failed to load ApplicationContext at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
~[spring-test-5.1.5.RELEASE.jar:5.1.5.RELEASE] at
org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
...
The problem is that you really don't have a JavaMailSender available (and you wouldn't want a real one during your tests). You have four options:
Register a mock/stub JavaMailSender bean in a test configuration.
Use auto-configuration to make your EmailSenderService itself #ConditionalOnBean(JavaMailSender.class) and register a stub if there isn't one (this is usually what I do for testing "does the system send transactional mail?").
In this case, since you're actually trying to test the EmailSenderService itself, set spring.mail.host: localhost in your application-test properties (or an annotation).
Don't use Spring DI at all here. The major advantage of constructor injection is that you can hand-instantiate your beans, and you could new up your EmailSenderService and its dependencies.
If I understand the intended scope of your test, #3 is probably the solution to go with for you.
Related
I have created a bean method in the main class:
#SpringBootApplication
#EnableScheduling
public class SpringApplication{
#Bean
Public String getCronValue(ServiceImpl service){
return service.getConfig().get("cron duration");
}
}
using this bean in a scheduled task:
#Component
public Class MySch{
#Scheduled(cron="#{getCronValue}")
public void schedulerMethod(){
//Do something
}
}
Now the problem is when I try to run JUnit tests #Bean GetCronValue is not initialized in test context and #Scheduled annotation throws an exception:
Update:-
It throws an exception:-
BeanCreationException: Error creating bean with name
'SchedulerMethod' : Initialization of bean failed; nested
exception is ' org. springframework.beans.
factory.Beanexpressio exception: Expression parsing
failed; nested exception is org. springframework.
expression.spel.SpelEvaluationException: EL1021E: A
problem occurred whilst attempting to access the
property ' getCronValue' : Error creating bean with name
'getCronValue' : Unsetisfied dependency expressed
through method 'getCronValue' parameter 0; nested
exception is org. springframework. beans. factory.
NoSuchBeanDefinitionException: No qualifying bean of
type 'com.pkg.service.ServiceImpl' available: expected at
least 1 bean which qualifies as a autowire candidate.
Dependemcy annotations: {}'
My Controller test class looks like:-
#Transactional
public class ControllerTest{
#MockBean
private Service service;
.
.
// test cases
}
How to resolve this issue.
I assume that you're using #SpringBootTest annotation.
When you test a Controller you may want narrow the tests to only the web layer by using #WebMvcTest. Any other dependencies required by the controller will be then mocked using #MockBean.
When #WebMvcTest is used Spring Boot instantiates only the web layer rather than the whole context. In an application with multiple controllers, you can even ask for only one to be instantiated for example.
#WebMvcTest(controllers =Controller.class)
public class ControllerTest{
#MockBean
private Service service;
#Autowired
private MockMvc mockMvc;
// test cases
}
I noticed that you have the #Transactional annotation in your example. This can indicate that you maybe giving too match responsibilities to your controller and may consider passing Database access related logic to a service/repostory/DAO
See https://spring.io/guides/gs/testing-web/
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.
I just started to work with Spring. I followd some tutorials to create a Spring Web MVC project which works. It is a simple project which displays some information as website using thymeleaf.
Now I wanted to add a couchbase database for storing data, I already worked with couchbase for simple .Net Apps. So I followed the tutorial on the official couchbase blog to create a service for the connection to couchbase. Couchbase with Spring-Boot and Spring Data
But when I try to autowire the service I get the following error message:
[main] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcher'
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode!
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data repositories in DEFAULT mode.
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 70ms. Found 1 repository interfaces.
[main] WARN org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'indexController': Unsatisfied dependency expressed through field 'buildingService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xplorg.model.BuildingService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
[main] ERROR org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'indexController': Unsatisfied dependency expressed through field 'buildingService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xplorg.model.BuildingService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
As far as I understand the message, found Spring the repository but it does not qualify as autowire candidate.
My WebConfig:
#Configuration
#EnableWebMvc
#ComponentScan("com.project.controller")
#ComponentScan("com.xplorg.model")
#EnableCouchbaseRepositories(basePackages = {"com.project.model"})
public class MvcWebConfig implements WebMvcConfigurer {
// Code for generating templateEngine/Resolver ...
}
My controller (here I try to autowire the service, not sure if it is the correct place, causes the exception):
#Controller
public class IndexController {
private int count = 0;
#Autowired
private BuildingService buildingService;
// This line causes the exception
#GetMapping("/")
public String index(Model model) {
model.addAttribute("message", "Welcome to Hello World");
return "index";
}
// Some more mapping no use of service
}
My BuildingService:
public interface BuildingService {
Building save(Building building);
Building findById(String buildingId);
List<Building> findByCompanyId(String companyId);
Building findByCompanyAndAreaId(String companyId, String areaId);
List<Building> findByCompanyIdAndNameLike(String companyId, String name, int page);
List<Building> findByPhoneNumber(String telephoneNumber);
Long countBuildings(String companyId);
}
My BuildingServiceImpl:
#Service("BuildingService")
public class BuildingServiceImpl implements BuildingService {
#Autowired
private BuildingRepository buildingRepository;
#Override
public List<Building> findByCompanyId(String companyId) {
return buildingRepository.findByCompanyId(companyId);
}
public List<Building> findByCompanyIdAndNameLike(String companyId, String name, int page) {
return buildingRepository.findByCompanyIdAndNameLikeOrderByName(companyId, name, new PageRequest(page, 20))
.getContent();
}
#Override
public Building findByCompanyAndAreaId(String companyId, String areaId) {
return buildingRepository.findByCompanyAndAreaId(companyId, areaId);
}
#Override
public List<Building> findByPhoneNumber(String telephoneNumber) {
return buildingRepository.findByPhoneNumber(telephoneNumber);
}
#Override
public Building findById(String buildingId) {
return buildingRepository.findById(buildingId).get();
}
#Override
public Building save(Building building) {
return buildingRepository.save(building);
}
#Override
public Long countBuildings(String companyId) {
return buildingRepository.countBuildings(companyId);
}
}
The order classes are simple copied from the tutorial.
Project sturcture:
src/main/java
com.project
config
MvcWebApplicationInitializer
MvcWebConfig
controller
IndexController
model
Area
Building
BuildingRepository
BuildingService
BuildingServiceImpl
EDIT
At the end of the error message the following line stands, could that mean I am missing a dependency?
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'couchbaseRepositoryOperationsMapping' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:769)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1221)
EDIT 2
After trying different approaches and reading more about Spring/Couchbase and Autowired, I now get the exception that No bean named 'couchbaseRepositoryOperationsMapping' available. The whole exception looks like this:
[main] INFO org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcher'
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Bootstrapping Spring Data repositories in DEFAULT mode.
[main] INFO org.springframework.data.repository.config.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 63ms. Found 1 repository interfaces.
[main] WARN org.springframework.web.context.support.AnnotationConfigWebApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'buildingServiceImpl': Unsatisfied dependency expressed through field 'buildingRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'buildingRepository': 'buildingRepository' depends on missing bean 'couchbaseRepositoryOperationsMapping'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'couchbaseRepositoryOperationsMapping' available
[main] ERROR org.springframework.web.servlet.DispatcherServlet - Context initialization failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'buildingServiceImpl': Unsatisfied dependency expressed through field 'buildingRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'buildingRepository': 'buildingRepository' depends on missing bean 'couchbaseRepositoryOperationsMapping'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'couchbaseRepositoryOperationsMapping' available
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
I have my java spring boot application and I'd like to write some tests using mockmvc; so this is the testing class:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = {IndexController.class})
#ComponentScan(basePackages={"com.sherlock.discoteque"})
#EnableJpaRepositories("com.sherlock.discoteque.repository")
#EntityScan(basePackages={"com.sherlock.discoteque"})
public class DiscotequeApplicationTests {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(webApplicationContext).build();
}
#Test
public void testAlbumInfo() throws Exception{
this.mockMvc.perform(get("/")).andExpect(status().isOk());
}
}
but when I execute the code I have the following error message:
Field albumRepository in com.sherlock.discoteque.controller.AlbumController required a bean of type 'javax.persistence.EntityManagerFactory' 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
'javax.persistence.EntityManagerFactory' in your configuration.
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'albumController': Unsatisfied
dependency expressed through field 'albumRepository'; nested exception
is org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'albumRepositoryImpl': Injection of
persistence dependencies failed; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'javax.persistence.EntityManagerFactory'
available
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'albumRepositoryImpl': Injection of
persistence dependencies failed; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'javax.persistence.EntityManagerFactory'
available
Which is weird, considering the fact that without the testing class everything works fine. This is the class AlbumRepositoryImpl
public class AlbumRepositoryImpl implements AlbumRepositoryCustom {
private final static String RECENT_ALBUMS_QUERY = "select * from album order by createdate desc limit ?";
#PersistenceContext
public EntityManager entityManager;
#Override
public List<Album> getRecentAlbums(int size) {
if(size<1){
throw new IllegalArgumentException();
}
Query query = entityManager.createNativeQuery(RECENT_ALBUMS_QUERY, Album.class);
query.setParameter(1, size);
return query.getResultList();
}
}
and inside the AlbumController I do have the attribute
#Autowired
private AlbumRepository albumRepository;
and I have the AlbumRepository interface as well (extended from JpaRepository). I really don't know what to do to make the web application running on test, could anybody help me?
In sample code , you are trying to autowire the context , however you have not provided the test configuration.
In your project you have defined JPA entity manager configuration , but in test file you are not providing that info. Spring won't be able to start the container till you don't provide the necessary configuration in test class.
You can take an idea from https://www.petrikainulainen.net/programming/spring-framework/integration-testing-of-spring-mvc-applications-configuration/
Try to set spring boot profile with annotation - #ActiveProfiles("you_profile")
Here's my test case for a Spring Controller
#RunWith(SpringRunner.class)
#WebMvcTest(value = MyController.class)
public class MyControllerTest {
#MockBean
private MyService myService;
}
So this is a unit test specifically for the methods in MyController. But when I run the test, Spring appears to begin instantiating OtherController and all of it's dependencies.
I have tried updating the above as
#RunWith(SpringRunner.class)
#WebMvcTest(value = MyController.class, excludeFilters = #ComponentScan.Filter(value= OtherController.class, type = FilterType.ANNOTATION))
public class MyControllerTest {
...
}
But spring still appears to wire it. Here's the error thrown by Spring as it tries to instantiate OtherController when I run the above test specifically.
2017-01-06 12:09:46.207 WARN 18092 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'otherController' defined in file [C:\..OtherController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'getOtherService' defined in com.my.myApplication: Unsatisfied dependency expressed through method 'getOtherService' parameter 0org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'getOtherService' defined in com.myOrg.MyServiceApplication: Unsatisfied dependency expressed through method 'getPositionService' parameter 0
What could be causing this?
Chances are that you are triggering the bean scanning by accident via a #Componentscan or the likes.
For instance, as explained in this answer, Spring may be detecting your "production" #SpringBootApplication Application class, with all the component scans it brings about. If so make sure you "overwrite" your "production" Application class with your "test" one putting...
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
...in a location that would override the "Production" Application.
For instance, if your IDE doesn't get messed by class name clashes:
"production" -> src/main/java/com/mycompany/Application.java
-> src/main/java/com/mycompany/controllers/MyController.java
"test" -> src/test/java/com/mycompany/Application.java
-> src/test/java/com/mycompany/controllers/MyControllerTest.java
As an alternative I also have found that in my case this works as well (i.e. placing the Application in the test controllers folder)
"production" -> src/main/java/com/mycompany/Application.java
-> src/main/java/com/mycompany/controllers/MyController.java
"test" -> src/test/java/com/mycompany/controllers/Application.java
-> src/test/java/com/mycompany/controllers/MyControllerTest.java