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/
Related
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?
I have this test class :
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
but in the class I want to test, I have this line :
#Autowired
AnnoyingServiceWhichIsADependency annoyingDependency;
So when I try to run the test class - I get this error :
java.lang.IllegalStateException: Failed to load ApplicationContext
and the cause by line seems to throw this up :
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ClassToBeTested': Unsatisfied dependency expressed through field 'AnnoyingServiceWhichIsADependency'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type '<package-path>.AnnoyingServiceWhichIsADependency' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I will add that the actual class does work, and does what it is meant to do, but I am having trouble making it work in the unit test world.
All help appreciated.
The reason a bean is not created for the dependency class is that you're using #WebMvcTest and not #SpringBootTest: only controllers and the MVC infrastructure classes are scanned. From the docs:
Can be used when a test focuses only on Spring MVC components.
Since it's an MVC test, you can mock the service dependency.
Example: https://reflectoring.io/spring-boot-web-controller-test/
#WebMvcTest is only going to scan the web layer- the MVC infrastructure and #Controller classes. That's it. So if your controller has some dependency to other beans from, e.g. form your service layer, they won't be found to be injected.
If you want a more comprehensive integration test, use #SpringBootTest instead of #WebMvcTest
If you want something closer to a unit test, mock your dependency.
Also note that Field injection (#Autowired directly on the field) is not recommended exactly for these reasons. I recommend you change to constructor injeciton ( add a constructor for Classtobetested and place the #Autowired annotation on it. ) Then for a unit test you can pass in a mock. Constructor injection leads to a more testable and configurable design.
Your test application context is trying to load your ClassToBeTested but is unable to find one of its dependencies and complains about it via that error. Basically you need to have a #Bean of that type in your test context. An option will be to create a TestConfig class which provides a Mock/Spy of that dependency via #Bean annotation. In your test you will have to load inside the context via the #ContextConfiguration annotation this test config you just created.
https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#spring-testing-annotation-contextconfiguration
Just mock that dependency. Assuming that AnnoyingServiceWhichIsADependency is an interface:
#RunWith(SpringRunner.class)
#WebMvcTest(ClassToBeTested.class)
public class ClassToBeTestedTests {
#Autowired
private MockMvc mockMvc;
#MockBean
private AnnoyingServiceWhichIsADependency annoyingDependency;
#Test
public void simpleTestMethodToGetClassWorking(){
Assert.assertTrue(true);
}
}
Use Mockito when and thenReturn methods to instruct the mock.
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")
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'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.