I have a main #SpringBootApplication which needs to scan a specific package in order to enable the JPA repositories, so I use #EnableJpaRepositories to specify that. Right now I'm implementing unit tests and I want to test the Controller component only, so I followed the tutorial in the official docs where they use #WebMvcTest(MyController.class) to test a controller with a service dependency.
The problem is that this is not working for me because it is trying to load the JpaRepositories that I specify in the main Spring Boot application (when I comment the #EnableJpaRepositories in the main class the test runs without problem).
I'm guessing I need to create a specific configuration for the test class so it can ignore the main configuration (since I only want to load the Controller and mock the service layer), but I don't know how to create such. I tried adding an empty configuration, but it is still failing with the same error:
#TestConfiguration
static class TestConfig {}
This is the error I get:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'failureTaskHandler': Unsatisfied dependency expressed through field 'myManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'msgManager': Unsatisfied dependency expressed through field 'inboundManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'inboundManager': Unsatisfied dependency expressed through field 'messageRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageRepository' defined in com.example.MessageRepository defined in #EnableJpaRepositories declared on MonitorApplication: Cannot create inner bean '(inner bean)#45e639ee' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#45e639ee': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
And my test class:
#WebMvcTest(MyController.class)
public class MyControllerTest {
#Autowired private MockMvc mvc;
#MockBean private MyService service;
// Tests here
// #Test
// public void...
}
MyController class:
#RestController
#CrossOrigin
#RequestMapping("/api")
#Slf4j
public class MyController {
#Autowired private MyService service;
#PostMapping(value = "/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody SearchResponse getOrders(#RequestBody SearchRequest orderSearchRequest) {
log.info("Receiving orders request...");
return service.getOrders(orderSearchRequest);
}
}
Quick solution
Remove #EnableJpaRepositories from your Spring Boot Application class. Use:
#SpringBootApplication
public class MainApplication {
}
in place of
#SpringBootApplication
#EnableJpaRepositories
public class MainApplication {
}
In this case Spring Boot will find Spring Data JPA on the classpath and uses auto-configuration to scan packages for the repositories.
Use #EnableJpaRepositories to scan a specific package
Use #NikolaiShevchenko solution (it is incorrect) with a separate configuration, but without explicit importing it, by #Import({ DataConfiguration.class }), (because tests will be explicitly import the configuration too) and let Spring Boot find your configuration during packages scan.
#SpringBootApplication
public class MainApplication {
}
#Configuration
#EnableJpaRepositories(basePackages = "com.app.entities")
public class JpaConfig {
}
Important
Don't forget to add basePackages property, if you put your configuration in a separate package.
Declare separate configuration
#Configuration
#EnableJpaRepositories
public class DataConfiguration { ... }
Import it into the application
#SpringBootApplication
#Import({ DataConfiguration.class })
public class MainApplication { ... }
but don't import into MyControllerTest
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 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/
i have a config class in a config package that looks like this:
package com.x.y.appName.config
#ComponentScan(basePackageClasses = { Application.class })
#Configuration
public class AppConfig {
my project is failing on build within SomeClass that uses the AppConfig bean, saying:
Error creating bean with name 'someClass': Unsatisfied dependency
expressed through field 'appConfig'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
But when I stdout print the list of beans Spring is aware of, it lists appConfig there
SomeClass is also in the config package, and looks like this:
package com.x.y.appName.config;
#Configuration
public class SomeClass implements WebMvcConfigurer {
#Autowired
AppConfig appConfig;
but if i add this to SomeClass, it builds fine and all tests pass:
#ComponentScan("com.x.y.appName.config")
in the past ive never needed to ComponentScan the same package that another bean is also declared in
again to clarify, i can bootRun the app just fine, but this spring error is throwing during build or test. do i need to add something to the unit tests? I dont have unit tests for either of the above classes as they would be too frivolous. So what could be going on? Do I need to annotate other unit tests somewhere?
I noticed that with #EnableWebMvc I'm getting the following error when running my tests: A ServletContext is required to configure default servlet handling. This issue is temporarily resolved by commenting out #EnableWebMvc then my tests all pass, however I want this in my web app.
I read in this post that I could put the #EnableWebMvc in another config class that isn't included in the tests(?). So I've tried this:
AppConfig.java
#Configuration
#ComponentScan(basePackages = "biz.martyn.budget")
#PropertySource("classpath:prod.properties")
#EnableTransactionManagement
public class AppConfig {
#Autowired
private Environment env;
#Bean(name = "dataSource", destroyMethod = "shutdown")
#Profile("prod")
public DataSource dataSourceForProd() {...
WebMvcConfig.java
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {...
Then in my tests I'm attempting:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#Transactional
public class FundRepositoryTest {...
However, I'm still seeing the same error in my tests. I know it's the #EnableWebMvc as they all pass when I remove this. Have I misunderstood something with how #ContextConfiguration annotation works? By the way, I'm using Spring version 4.2.2.RELEASE for all my spring-* dependencies if that helps.
Below is also the error I'm seeing in my test run:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
I'm still not sure why the #ContextConfiguration annotation isn't only accepting the class(es) I provide but I have found that #WebAppConfiguration added to each test class provides the context required:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#Transactional
#WebAppConfiguration // <-- added this
public class FundRepositoryTest {...
It's an additional annotation I need to add though but my tests run now.
I'm trying to build a rest api with Spring and Embedded Elastic. I'm getting an NoSuchBeanDefinitionException when trying to start my application.
Currently, I have this for wiring the elastic db:
#Configuration
public class EsConfig {
Node node;
#Bean
public Client es() {
node = nodeBuilder().local(true).node();
return node.client();
}
(Destructor)
}
and in the controller:
#RestController
public class Controller {
#Autowired
public Client elasticSearchClient;
...
}
But when I start it up, I get this exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'controller': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: public org.elasticsearch.client.Client package.Controller.elasticSearchClient;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.elasticsearch.client.Client] 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've tried a few different annotations but I'm obviously way off.
No qualifying bean of type [some.Thing] means that spring knowns no class that is applicable for this interface.
Reasons for that can be
The class that has the #Bean method is not a #Configuration class
The #Configuration class is not picked up by the classpath component scanner.
Spring boot by default will only scan the child package hierarchy of the #SpringBootApplication. If you want to include code outside of that you can change the scanning behavior via the #ComponentScan annotation.
#SpringBootApplication
#ComponentScan(basePackageClasses = {MyApp.class, SomeOtherClassInARootPackage.class})
public class MyApp {
...
Would add the package (and sub packages) of some other class, while keeping the packages of the application scanned as well.