Spring WebMvcTest is not creating MockBeans in tested Controller - java

I have a SpringBoot application an I have a Unit test for a Rest Controller. This unit test uses the #SpringBootTest annotation.
My Controller:
#CrossOrigin(origins = "*")
#RestController
#Validated
public class EngineeringProfileController {
#Autowired
private EngineeringProfileService engineeringProfileService;
#Autowired
private AuthenticationTokenParser authenticationTokenParser;
...
}
My Unit test:
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
#AutoConfigureMockMvc(addFilters = false)
#SpringBootTest // It is working!
//#WebMvcTest(value = EngineeringProfileController.class) // It does not work!
public class EngineeringProfileControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private EngineeringProfileService engineeringProfileServiceMock;
#MockBean
private AuthenticationTokenParser authenticationTokenParserMock;
...
}
When I run the test, it OK, but it takes a lot of time to load. Just recently I understood that #SpringBootTest Load all dependencies. So I tried use #WebMvcTest annotation (that is commented in code above). However, I replace #SpringBootTest by #WebMvcTest, I got this error:
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'engineeringProfileEmailSender':
Unsatisfied dependency expressed through field 'userService'; nested
exception is
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'userService': Unsatisfied dependency
expressed through field 'userProxy'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'profilemanagement.external.proxies.UserProxy': Unexpected exception
during bean creation; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type
'org.springframework.cloud.openfeign.FeignContext' available
Strangely, this engineeringProfileEmailSender is not in controller but in EngineeringProfileService. It is as if instead of creating a EngineeringProfileService mock for EngineeringProfileController it is actualling using the real class. I tried to googled how to solve this issue but nothing that seemed to fit worked.
What is missing here?

Related

How to initialize bean method defined in Main class into test context

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/

How to exclude #EnableJpaRepositories from test?

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

Can't run test on spring boot application because of injection of persistence dependencies failed

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")

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

Autowiring Embedded Elastic with Spring

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.

Categories

Resources