I am new to spring boot and I am writing Junit for my REST Api.so many annotation are required by my class to run Junit. So I planned to put all annotation in one custom annotation and use that custom annotation in my Junit class. But now my Autowire is stopped working.
I tried to create custom annotation and used that in my Junit class
but now #Autowire annotation has stopped to work.
Below is code of custom annotation:
#Inherited
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.CLASS)
#Documented
#Category(UnitTest.class)
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {AptDataApiApplicationRepo.class })
#TestPropertySource("classpath:application-test.properties")
public #interface RepoTestConfig {
}
// My JUnit class code
#RepoTestConfig
public class HolidayRepoTest {
#Autowired
private IHolidayRepo iHolidayRepo;
}
// here spring throwing an exception when spring context is trying to get up
Exception coming is :
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.demo.repository.IHolidayRepo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
#Note: When I am putting all annotation in HolidayRepoTest class then every thing is fine.
Related
I created an Integration test to test the new feature I just added but the Spring wiring is not working. The unit tests all work and the existing Spring integration tests still work but I am unable to Autowire my new class
Here is the error message –
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.xxx.xxx.etc.MyNewClassTest’: Unsatisfied dependency expressed through field 'sut'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xxx.xxx.etc.MyNewClass ' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
The new class –
#Slf4j
#Component
public class MyNewClass extends AbstractRetryJob<Event> {
My test -
#ExtendWith(SpringExtension.class)
class MyNewClassTest {
#Autowired private MyNewClass sut;
Any idea on what is going wrong?
Adding #ExtendWith(SpringExtension.class) is not enough to create the Spring context. You need to add #SpringBootTest to your MyNewClassTest. Per the appropriate comment, you can drop #ExtendWith(SpringExtension.class)
#SpringBootTest
class MyNewClassTest {
#Autowired private MyNewClass sut;
}
I'm trying to write a integration test against my Spring Data API with the following test configuration.
eureka:
client:
enabled: false
[..] # No other configuration part that affects discovery/eureka client
This is my test class
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
#Transactional
class FooAPITest {
#Test
void contextLoads() {
}
}
However I have a component which injects the EurekaClient to get a service instance from it
#Component
public class ServiceClient {
#Autowired
public ServiceClient(#Qualifier("eurekaClient") EurekaClient eurekaClient) {
URI serviceUri = URI.create(eurekaClient.getNextServerFromEureka("service", false).getHomePageUrl());
}
}
So as of this service my application is not able to load the ApplicationContext.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.netflix.discovery.EurekaClient' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Qualifier(value="eurekaClient")}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)
... 81 more
What I've tried so far
I thought about setting up a custom ContextConfiguration to exclude the ServiceClient as it is not needed in the test class. However I need to include a Configuration File which autowires the EntityManager but when I use #SpringBootApplication(classes = {Configuration.class}) the EntityManager can not be injected. This Configuration looks like that:
#Configuration
class Configuration {
#Autowired
EntityManager entityManager;
}
This produces the same error but with EntityManager Bean:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' 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:1695)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
... 81 more
Current Workaround
Currently I'm avoiding the problem by mocking the ServiceClient but I want to get rid of that code smell.
#MockBean
ServiceClient serviceClient;
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(FooAPITest.class);
}
Another workaround would be to mark the Injected Beans as not required but I don't find that practicable only to make the tests work.
What is a proper way to solve this problem?
You can try mocking the EurekaClient in your test:
#SpringBootTest
#AutoConfigureMockMvc(addFilters = false)
#Transactional
class FooAPITest {
#MockBean
private EurekaClient eurekaClient;
#Test
void contextLoads() {
}
}
This will create the EurekaClient as a mocked bean in the ApplicationContext to be injected into your service.
If you have other tests that initialize the Spring ApplicationContext, you can create a separate configuration class within the application package to be scanned (using #ConditionalOnMissingBean annotation to cover all bases):
#Configuration
public class MockEurekaConfiguration {
#Bean
#ConditionalOnMissingBean
public EurekaClient eurekaClient() {
return Mockito.mock(EurekaClient.class);
}
}
I want to test a Spring #Service.
This service has method using a JpaRepository autowired.
That is the code of the simple service.
#Service
public class PersonneService {
#Autowired
PersonneRepository personneRepository;
public Personne createPersonne(Personne personne) {
return personneRepository.save(personne);
}
I am trying to test it but i have an error
Unsatisfed dependency expressed for the service.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.symit.gmah.emprunt.services.PersonneHandleService': Unsatisfied dependency expressed through field 'personneService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.symit.gmah.emprunt.services.PersonneService' available: expected at least 1 bean which qualifies as autowire candidate. .....
That is the code of my test.
#RunWith(SpringRunner.class)
#DataJpaTest
public class PersonneHandleService {
#Autowired
PersonneService personneService;
#Test
public void PersonneServiceCreateTest() {
Personne personne = new Personne("John","Doe","43343");
personne = personneService.createPersonne(personne);
assertNotNull(personne.getId());
}
Can you help me and explain me what I have to do.
Thanks
PS: I am using embeded H2 Database;
That is my configuration:
# Enabling H2 Console
spring.h2.console.enabled=true
jdbc.driverClassName=org.h2.Driver
#jdbc.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
jdbc.url=jdbc:h2:~/gmahdb;DB_CLOSE_DELAY=-1
jdbc.username=sa
jdbc.password=sa
You still have to add your services to the Spring context. You now only setup your JPA (entities and repositories).
As an alternative, you could also just use #SpringBootTest in stead of #DataJpaTest. This loads the entire application for testing.
Depending on the size and number of dependencies of your application this might not be the optimal scenario.
From the documentation of DataJpaTest:
Annotation that can be used in combination with
#RunWith(SpringRunner.class) for a typical JPA test. Can be used when
a test focuses only on JPA components. Using this annotation will
disable full auto-configuration and instead apply only configuration
relevant to JPA tests.
and...
If you are looking to load your full application configuration, but
use an embedded database, you should consider #SpringBootTest combined
with #AutoConfigureTestDatabase rather than this annotation.
I am attempting the following unit test of a DAO.
I am unable to get the DataSource recognized.
Can I get a tip on how to resolve this?
Details below
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class EntityDaoTest {
#Autowired
protected EntityDao entityDao;
#Before
public void setup()
{
}
#Test
public void test() throws InternalServerException
{
List<Entity> entities = entityDao.list();
assert(entities.size()==0);
}
}
The relevant aspects of the DAO class are as follows
#Repository
public class EntityDao extends GenericDao<Entity>{
public EntityDao(DataSource dataSource) {/.../}
}
My src/test/resources/application.properties file is as follows
# Database
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=dbuser
spring.datasource.password=dbpass
Trace from running as JUnit test in Eclipse
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityController': Unsatisfied dependency expressed through field 'entityDao': Error creating bean with name 'entityDao' defined in file .../target/classes/hitstpa/dao/EntityDao.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type [javax.sql.DataSource] found for dependency [javax.sql.DataSource]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {};
...
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityDao' defined in file [/home/fmason/workspace/hitstpa/target/classes/hitstpa/dao/EntityDao.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type [javax.sql.DataSource] found for dependency [javax.sql.DataSource]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency [javax.sql.DataSource]: expected at least 1 bean which qualifies as autowire candidate for this dependency. ...`
Application structure
-src
--main
---java
----Application.java
----com
----hitstpa
-----controller
-----dao
------EntityDao.java
-----model
---resources
----application.properties
--test
---java
----hitstpa
-----dao
------EntityDaoTestDOTjava
---resources
----applicationDOTproperties
First of all for integration tests you need an integration Db with some fixed data.
Now you need to create a configuration class which will create the
integration test specific dependencies(I have named it as DbConfig
.java)
Next is add #ContextConfiguration annotation to the integration test
class and provide DbConfig.java, so that when test runs it will
create the datasource dependency and inject it to the container
Sample Code
#Configuration
public class DbConfig {
#Bean
public DataSource dataSource() {
//Create the DataSource with integration-DB properties
return dataSource;
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#ContextConfiguration(classes=DbConfig.class)
public class EntityDaoTest {
}
For the record, I believe this is not a good unit test. This test requires that a mysql databases exists on localhost.
Anyhow, the errors suggest that the Spring Context isn't loaded correctly. When using SpringBootTest, Spring looks for the configuration using the test's package as root. So, if it's lower than your Configuration classes, it won't them.
Take a look at Spring's documentation:
The search algorithm works up from the package that contains the test
until it finds a #SpringBootApplication or #SpringBootConfiguration
annotated class. As long as you’ve structure your code in a sensible
way your main configuration is usually found.
Solution:
You can either move your tests to the same level as your SpringBoot Main class or change it to: #SpringBootTest(classes = YourSpringBootMainClass.class)
Have a very light Spring Boot 1.4 project, generated from start.spring.io.
Trying to run an intergration test for #RestController with #RequestBody using TestRestTemplate, but there's no success because of a startup exception.
The only configuration class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Configuration file application.properties has almost nothing except of security.ignored=/** for the test purposes.
The test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#DataJpaTest
public class MyControllerTest {
private Logger log = Logger.getLogger(getClass());
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private TestEntityManager entityManager;
#Before
public void init() {
log.info("Initializing...");
}
#Test
public void addTest() throws Exception {
log.info("MyController add test starting...");
// restTemplate usage
log.info("MyController add test passed");
}
}
... but during the test startup I get the following exception:
ERROR 6504 --- [ main] o.s.test.context.TestContextManager : Caught exception while allowing TestExecutionListener [org.springframework.boot.test.autoconfigure.AutoConfigureReportTestExecutionListener#5444f1c3] to prepare test instance [com.myproject.controllers.MyControllerTest#5d2bc446]
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.myproject.controllers.MyControllerTest': Unsatisfied dependency expressed through field 'restTemplate': No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate] found for dependency [org.springframework.boot.test.web.client.TestRestTemplate]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.test.web.client.TestRestTemplate] found for dependency [org.springframework.boot.test.web.client.TestRestTemplate]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.3.2.RELEASE.jar:4.3.2.RELEASE]
According to the doc it's not required to configure TestRestTemplate anywhere. However, I've added the latest Apache Http Client to the classpath as it recommended.
What have I missed?
You are specifying #DataJpaTest which tells Spring to exclude any wiring of the web context for the tests. As such there is no TestRestTemplate created. Read this blog for more details around testing slices of your application: https://spring.io/blog/2016/04/15/testing-improvements-in-spring-boot-1-4#testing-application-slices
I had a similar problem running the main class on Eclipse using Serenity BDD tests with a spring-boot. It starts to fail after I have added the spring-boot-test-autoconfigure test dependency. That happens because Eclipse put everything in just one classloader. In order to fix this error, I have created one configuration class overriding the default behavior of the spring-boot. This code was based in one spring class (the scope not is public) SpringBootTestContextCustomizer.TestRestTemplateFactory
#TestConfiguration
public class TestConfig {
// Overriding Default Spring Boot TestRestTemplate to allow
// execute the main method from Eclipse (mixed Classloader)
#Bean
#Primary
public TestRestTemplate testRestTemplate(ApplicationContext context, RestTemplateBuilder templateBuilder) {
final AbstractConfigurableEmbeddedServletContainer container = context.getBean(AbstractConfigurableEmbeddedServletContainer.class);
final boolean sslEnabled = container.getSsl() != null && container.getSsl().isEnabled();
final TestRestTemplate template = new TestRestTemplate(templateBuilder.build(), null, null, sslEnabled? new HttpClientOption[]{}: new HttpClientOption[]{HttpClientOption.SSL});
template.setUriTemplateHandler(new LocalHostUriTemplateHandler(context.getEnvironment(), sslEnabled ? "https" : "http"));
return template;
}
}