How to use #Autowired in SpringBoot unit test - spring-boot

I am trying to unit test (using JUnit5 jupiter) a class developed in Java with Spring Boot that I would like to use the #Autowired annotation for convenience.
A very simplified version of it is as follow:
import org.springframework.stereotype.Component;
#Component
public class Demo {
public String get() {
return "hello";
}
}
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
#SpringJUnitConfig
class DemoTest {
#Autowired private Demo sut;
#Test
void Test() {
Assertions.assertEquals("hello", sut.get());
}
}
When I run the test this error occurs:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.demo.DemoTest': Unsatisfied dependency expressed through field 'sut'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.Demo' 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.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:417)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:119)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
...
How to prevent it?
To be noted: for this test, I do not want to start the full application using #SpringBootTest
Thanks,

Usually when you use #ContextConfiguration (which is a significant part of the stereotype #SpringJUnitConfig annotation) you should specify the configuration class from which the "Demo" component will be resolved.
Otherwise spring won't know which classes to load.
So you should basically create a configuration class (annotated with #Configuration) And specify what do you want to load there, for example with #ComponentScan
Haven't tested it but something like this should work:
#SpringJUnitConfig(MyTestConfig.class)
public class MyTest {
#Autowired Demo sut;
public void test() {... sut.doSomething(...); ...}
}
#Configuration
#ComponentScan(...) // here you should specify how to
// load the Demo class and maybe other classes as well
public class MyTestConfig {
}
Of course if instead of #Component you already use Java Config and define Demo class there you won't need to create this auxiliary MyTestConfig class and can load the configuration with the Demo class right away

Related

Why is my configuration test class not working?

I am trying to write unit test using junit for my service configuration class. I have existing code that works in other module, but it doesn't work on this module for some reason and I cannot figure this out. Here is my code:
ServiceConfig class:
package config.service;
import service.Service;
import service.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ServiceConfig {
#Autowired
private Service service;
#Bean
public Service service() {
return new serviceImpl();
}
}
Service interface:
package service;
public interface Service {
void search() throws Exception;
}
ServiceImpl class:
package service;
public class ServiceImpl implements Service {
#Override
public void search() throws Exception {
return null;
}
}
ServiceConfigTest class:
package config.service;
import service.Service;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertNotNull;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = { ServiceConfig.class })
public class ServiceConfigTest {
#Autowired
private Service service;
#Test
public void service() {
assertNotNull(service);
}
}
and here is the Exception:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ServiceConfig': Unsatisfied dependency expressed through field 'Service'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.nuance.powershare.dispatchreporter.service.Service' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I don't have too much experience with spring and configuration classes. However, this seems legit to me, I basically followed the code that was already working in other module. My manager also cannot find what is wrong.
The above exception is caused, when we did not create a bean of the type it will raise an exception "Error creating bean with name 'className'.
I tried the same code it's working for me. However, you don't need to create Service Config to create a bean of ServiceImpl just annotate ServiceImpl with #Service and you can test it subsequently.
#Service
public class ServiceImpl implements Service {
#Override
public void search() throws Exception {
}
}
and avoid using the predefined names(ex: Service) for the class name.

No qualifying bean of type repository when running test but not main application

I'm developing a Spring Boot application following TDD methodology. I've created the main classes (controller, service and repository) this way:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class CrimeServiceImpl implements CrimeService{
#Autowired
private CrimeRepository repository;
...
Controller:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class CrimeController {
#Autowired
private CrimeServiceImpl service = new CrimeServiceImpl();
Repository:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CrimeRepository extends JpaRepository<Crime, Long>{
}
This is the project structure:
If I run the application normally, no error. The classes' methods are empty. Then I've created a test class like this:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = CrimeServiceImpl.class)
#ComponentScan("com.springmiddleware")
#AutoConfigureMockMvc
#SpringBootTest
public class TestCrimeService {
//Calling method getAllCrimes works
#Test
public void returnAllCrimesExists() throws NoSuchMethodException, SecurityException {
List<Crime> list = new ArrayList<>();
assertTrue(this.service.getAllCrimes() == list);
}
And if I run this, the following error is shown and the test fails:
NoSuchBeanDefinitionException: No qualifying bean of type 'com.springmiddleware.repository.CrimeRepository' available: expected at least 1 bean which qualifies as autowire candidate.
I've checked all annotations and it seems to me that all is ok, and I thought if I missed something, even in the normal run the application would fail. What did I got wrong?
I wanted also to make a test class for a JPARepository, and I also encountered the same error message:
NoSuchBeanDefinitionException: No qualifying bean of type
'SomethingRepository' available:
expected at least 1 bean which qualifies as autowire candidate.
I could make it work by adding the 2 following annotations on top of the test class:
#EnableJpaRepositories(basePackageClasses = SomethingRepository.class) // repository
#EntityScan(basePackageClasses = Something.class) // entity of the repository
Now it looks like:
#RunWith(SpringRunner.class)
#EnableJpaRepositories(basePackageClasses = SomethingRepository.class) // repository
#EntityScan(basePackageClasses = Something.class) // entity of the repository
#SpringBootTest(classes = MyDbUnitTestApp.class) // does some #ComponentScan and #EntityScan on the repositories/entities package, and #EnableAutoConfiguration
#ActiveProfiles(Profiles.myTestProfile)
#DatabaseSetup(value = {
"/datasets/dataset1.xml" }, type = DatabaseOperation.CLEAN_INSERT)
public class SomethingRepositoryTest {
#Autowired
private SomethingRepository sut;
#Test
public void findById() {
Something something= sut.findById(1L);
Assert.assertEquals("foobar", something.getName());
}
}

Spring Actuator - How to exclude HealthEndpoint from autowiring in test

I am trying to run a test but I got an exception when I try to run it Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.actuate.endpoint.HealthEndpoint' available:
I have two packages com.test.data and com.test.health in my main folder. My main Application class is in com.test.data. In com.test.health I have one class with name StartupHousekeeper implements ApplicationListener<ApplicationReadyEvent> and autowired HealthEndpoint there as below
#Component
public class StartupHousekeeper implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private HealthEndpoint healthEndpoint;
}
In test I have ControllerIntegrationTest which is config class and code is below. Even though I did not add com.test.health package it is still throwing NoSuchBeanDefinitionException.
#Profile("controller-integration-test")
#ComponentScan(basePackages = {"com.test.data"})
#Configuration
public class ControllerIntegrationTest {
}
My actual test class is below
#TestExecutionListeners(MockitoTestExecutionListener.class)
#ActiveProfiles({"controller-integration-test"})
#ContextConfiguration(classes = ControllerIntegrationTest.class)
#WebMvcTest(value = AdminUserController.class, secure = false, excludeAutoConfiguration = {HealthIndicatorAutoConfiguration.class, HibernateJpaAutoConfiguration.class, FlywayAutoConfiguration.class, DataSourceAutoConfiguration.class})
public class AdminUserControllerTest extends AbstractTestNGSpringContextTests implements TestConstants {}
When I try to run it org.springframework.test.context.testng.AbstractTestNGSpringContextTests#springTestContextPrepareTestInstance failed to run due to above mentioned exception

Spring Boot #autowired does not work, classes in different package

I have a Spring boot application.
I get the following error
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'birthdayController': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.esri.birthdays.dao.BirthdayRepository
com.esri.birthdays.controller.BirthdayController.repository; nested
exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.esri.birthdays.dao.BirthdayRepository]
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)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
~[spring-beans-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at or
Following is code of my Repository class
package com.esri.birthdays.dao;
import com.esri.birthdays.model.BirthDay;
public interface BirthdayRepository extends MongoRepository<BirthDay,String> {
public BirthDay findByFirstName(String firstName);
}
Following is controller.
package com.esri.birthdays.controller;
#RestController
public class BirthdayController {
#Autowired
private BirthdayRepository repository;
Works if they are in same package. Not sure why
When you use #SpringBootApplication annotation in for example package
com.company.config
it will automatically make component scan like this:
#ComponentScan("com.company.config")
So it will NOT scan packages like com.company.controller etc.. Thats why you have to declare your #SpringBootApplication in package one level prior to your normal packages like this: com.company OR use scanBasePackages property, like this:
#SpringBootApplication(scanBasePackages = { "com.company" })
OR componentScan:
#SpringBootApplication
#ComponentScan("com.company")
Just put the packages inside the #SpringBootApplication tag.
#SpringBootApplication(scanBasePackages = { "com.pkg1", "com.pkg2", .....})
Let me know.
Try annotating your Configuration Class(es) with the #ComponentScan("com.esri.birthdays") annotation.
Generally spoken: If you have sub-packages in your project, then you have to scan for your relevant classes on project-root. I guess for your case it'll be "com.esri.birthdays".
You won't need the ComponentScan, if you have no sub-packaging in your project.
Try this:
#Repository
#Qualifier("birthdayRepository")
public interface BirthdayRepository extends MongoRepository<BirthDay,String> {
public BirthDay findByFirstName(String firstName);
}
And when injecting the bean:
#Autowired
#Qualifier("birthdayRepository")
private BirthdayRepository repository;
If not, check your CoponentScan in your config.
Spring Boot will handle those repositories automatically as long as they are included in the same package (or a sub-package) of your #SpringBootApplication class. For more control over the registration process, you can use the #EnableMongoRepositories annotation. spring.io guides
#SpringBootApplication
#EnableMongoRepositories(basePackages = {"RepositoryPackage"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
In my case #component was not working because I initialized that class instance by using new <classname>().
If we initialize instance by conventional Java way anywhere in code, then spring won't add that component in IOC container.
For this kind of issue, I ended up in putting #Service annotation on the newly created service class then the autowire was picked up.
So, try to check those classes which are not getting autowired, if they need the corresponding required annotations(like #Controller, #Service, etc.) applied to them and then try to build the project again.
By default, in Spring boot applications, component scan is done inside the package where your main class resides. any bean which is outside the package will not the created and thus gives the above mentioned exception.
Solution: you could either move the beans to the main spring boot class(which is not a good approach) or create a seperate configutation file and import it:
#Import({CustomConfigutation1.class, CustomConfiguration2.class})
#SpringBootpplication
public class BirthdayApplication {
public static void main(String [] args) {
springApplication.run(BirthdayApplication.class, args );
}
}
Add beans to these CustomConfiguration files.
I had the same problem. It worked for me when i removed the private modifier from the Autowired objects.
Another fun way you can screw this up is annotating a setter method's parameter. It appears that for setter methods (unlike constructors), you have to annotate the method as a whole.
This does not work for me:
public void setRepository(#Autowired WidgetRepository repo)
but this does:
#Autowired public void setRepository(WidgetRepository repo)
(Spring Boot 2.3.2)
There will definitely be a bean also containing fields related to Birthday
So use this and your issue will be resolved
#SpringBootApplication
#EntityScan("com.java.model*") // base package where bean is present
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.test.springboot;
#SpringBootApplication
#ComponentScan(basePackages = "com.test.springboot")
public class SpringBoot1Application {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(SpringBoot1Application.class, args);
=====================================================================
package com.test.springboot;
#Controller
public class StudentController {
#Autowired
private StudentDao studentDao;
#RequestMapping("/")
public String homePage() {
return "home";
}
When I add #ComponentScan("com.firstday.spring.boot.services") or scanBasePackages{"com.firstday.spring.boot.services"} jsp is not loaded. So when I add the parent package of project in #SpringBootApplication class it's working fine in my case
Code Example:-
package com.firstday.spring.boot.firstday;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = {"com.firstday.spring.boot"})
public class FirstDayApplication {
public static void main(String[] args) {
SpringApplication.run(FirstDayApplication.class, args);
}
}

Spring #ContextHierarchy with custom initializer using addActiveProfile gives NoSuchBeanDefinitionException

We are using #ContextHierarchy in our tests to enable context caching but allow some texts to customise the context. I have a problem that one test needs to activate a specific Spring profile which activates a bean which is injected back into the test.
I have a feeling this is not possible but am looking for confirmation. Logically the parent context has already been initialised, so am willing to believe Spring will have a problem re-wiring the parent context based on a profile being activated in a test that is essentially just adding a child context through #ContextHierarchy. But I couldn't find anyone describing this problem after some googling.
The base test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy(
#ContextConfiguration(classes = BaseConfig.class, initializers = TestContextInitialiser.class)
)
public abstract class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {
...
}
The concrete test class looks like this:
#ContextHierarchy(
#ContextConfiguration(initializers = NotificationTest.CustomInitializer.class)
)
public class NotificationTest extends BaseTest {
#Autowired
private EventsConfig eventsConfig; // bean that needs to be activated via profile
...
public static class CustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getEnvironment().addActiveProfile("EVENTS_ENABLED");
}
}
}
The EventsConfig class uses #Profile...
#Component
#Profile("EVENTS_ENABLED")
public class EventsConfig {
...
}
And the error from Spring:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [xxx.EventsConfig] 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 would expect that Spring would at least warn/error in this situation and explain that the parent context cannot be modified with new profiles.
Any input/guidance appreciated. Is this or something similar possible? It will be a pain to have to refactor this test to avoid use of the base class.
Thanks

Resources